Created
October 14, 2022 14:47
-
-
Save SerafimArts/7d223310d55c330fa0da72f982ddc0f7 to your computer and use it in GitHub Desktop.
bin pack/unpack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
benchInt8 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 0.972μs | ±22.36% | | |
| PackBench | pack | 1.336μs | ±25.69% | | |
+-----------+------+---------+---------+ | |
benchUInt8 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 0.729μs | ±21.43% | | |
| PackBench | pack | 1.369μs | ±24.68% | | |
+-----------+------+---------+---------+ | |
benchInt16 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.461μs | ±18.23% | | |
| PackBench | pack | 1.345μs | ±24.70% | | |
+-----------+------+---------+---------+ | |
benchUInt16 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.275μs | ±25.66% | | |
| PackBench | pack | 1.756μs | ±18.53% | | |
+-----------+------+---------+---------+ | |
benchInt32 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 2.033μs | ±19.11% | | |
| PackBench | pack | 1.386μs | ±28.92% | | |
+-----------+------+---------+---------+ | |
benchUInt32 | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.828μs | ±25.15% | | |
| PackBench | pack | 1.795μs | ±3.83% | | |
+-----------+------+---------+---------+ | |
benchInt64 (insignificant benchmark: Code is identical) | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.358μs | ±27.14% | | |
| PackBench | pack | 1.363μs | ±28.97% | | |
+-----------+------+---------+---------+ | |
benchUInt64 (insignificant benchmark: Code is identical) | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.751μs | ±25.10% | | |
| PackBench | pack | 1.779μs | ±27.52% | | |
+-----------+------+---------+---------+ | |
benchFloat32 (insignificant benchmark: Code is identical) | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.764μs | ±21.66% | | |
| PackBench | pack | 1.766μs | ±27.28% | | |
+-----------+------+---------+---------+ | |
benchFloat64 (insignificant benchmark: Code is identical) | |
+-----------+------+---------+---------+ | |
| benchmark | set | mode | rstdev | | |
+-----------+------+---------+---------+ | |
| PackBench | raw | 1.761μs | ±24.82% | | |
| PackBench | pack | 1.763μs | ±25.70% | | |
+-----------+------+---------+---------+ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Bic\Bin; | |
final class Converter implements ParserInterface, SerializerInterface | |
{ | |
private Endianness $endianness; | |
public function __construct( | |
Endianness $endianness = null | |
) { | |
$this->endianness = $endianness ?? Endianness::current(); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt8(string $data): int | |
{ | |
$value = \ord($data[0]); | |
return $value & 0x80 ? $value - 0x100 : $value; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromInt8(int $data): string | |
{ | |
return \chr($data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt8(string $data): int | |
{ | |
return \ord($data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt8(int $data): string | |
{ | |
return \chr($data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt16(string $data): int | |
{ | |
$value = \ord($data[0]) | |
| \ord($data[1]) << 8; | |
return \ord($data[1]) & 0x80 ? $value - 0x1_0000 : $value; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromInt16(int $data): string | |
{ | |
return \pack('s', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt16(string $data, Endianness $endianness = null): int | |
{ | |
if (($endianness ?? $this->endianness) === Endianness::LITTLE) { | |
return \ord($data[0]) | (\ord($data[1]) << 8); | |
} | |
return \ord($data[1]) | (\ord($data[0]) << 8); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt16(int $data, Endianness $endianness = null): string | |
{ | |
if (($endianness ?? $this->endianness) === Endianness::LITTLE) { | |
return \chr($data >> 0) . \chr($data >> 8); | |
} | |
return \chr($data >> 8) . \chr($data >> 0); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt32(string $data): int | |
{ | |
$value = \ord($data[0]) | |
| \ord($data[1]) << 8 | |
| \ord($data[2]) << 16 | |
| \ord($data[3]) << 24; | |
return \ord($data[3]) & 0x80 ? $value - 0x1_0000_0000 : $value; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromInt32(int $data): string | |
{ | |
return \pack('l', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt32(string $data, Endianness $endianness = null): int | |
{ | |
if (($endianness ?? $this->endianness) === Endianness::LITTLE) { | |
return \ord($data[0]) | |
| \ord($data[1]) << 8 | |
| \ord($data[2]) << 16 | |
| \ord($data[3]) << 24; | |
} | |
return \ord($data[3]) | |
| \ord($data[2]) << 8 | |
| \ord($data[1]) << 16 | |
| \ord($data[0]) << 24; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt32(int $data, Endianness $endianness = null): string | |
{ | |
if (($endianness ?? $this->endianness) === Endianness::LITTLE) { | |
return \chr($data >> 0) | |
. \chr($data >> 8) | |
. \chr($data >> 16) | |
. \chr($data >> 24); | |
} | |
return \chr($data >> 24) | |
. \chr($data >> 16) | |
. \chr($data >> 8) | |
. \chr($data >> 0); | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function toInt64(string $data): int | |
{ | |
[1 => $int64] = \unpack('q', $data); | |
return $int64; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromInt64(int $data): string | |
{ | |
return \pack('q', $data); | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function toUInt64(string $data, Endianness $endianness = null): int | |
{ | |
[1 => $uint64] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'P', | |
Endianness::BIG => 'J', | |
default => 'Q', | |
}, $data); | |
return $uint64; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromUInt64(int $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'P', | |
Endianness::BIG => 'J', | |
default => 'Q', | |
}, $data); | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function toFloat32(string $data, Endianness $endianness = null): float | |
{ | |
[1 => $float] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'g', | |
Endianness::BIG => 'G', | |
default => 'f', | |
}, $data); | |
return $float; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromFloat32(float $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'g', | |
Endianness::BIG => 'G', | |
default => 'f', | |
}, $data); | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function toFloat64(string $data, Endianness $endianness = null): float | |
{ | |
[1 => $double] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'e', | |
Endianness::BIG => 'E', | |
default => 'd', | |
}, $data); | |
return $double; | |
} | |
/** | |
* TODO Replace pack/unpack to optimized binary algo | |
* | |
* {@inheritDoc} | |
*/ | |
public function fromFloat64(float $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'e', | |
Endianness::BIG => 'E', | |
default => 'd', | |
}, $data); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Bic\Bin; | |
final class NativeConverter implements ParserInterface, SerializerInterface | |
{ | |
private Endianness $endianness; | |
public function __construct( | |
Endianness $endianness = null | |
) { | |
$this->endianness = $endianness ?? Endianness::current(); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt8(string $data): int | |
{ | |
[1 => $int8] = \unpack('c', $data); | |
return $int8; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromInt8(int $data): string | |
{ | |
return \pack('c', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt8(string $data): int | |
{ | |
[1 => $uint8] = \unpack('C', $data); | |
return $uint8; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt8(int $data): string | |
{ | |
return \pack('C', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt16(string $data): int | |
{ | |
[1 => $int16] = \unpack('s', $data); | |
return $int16; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromInt16(int $data): string | |
{ | |
return \pack('s', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt16(string $data, Endianness $endianness = null): int | |
{ | |
[1 => $uint16] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'v', | |
Endianness::BIG => 'n', | |
default => 'S', | |
}, $data); | |
return $uint16; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt16(int $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'v', | |
Endianness::BIG => 'n', | |
default => 'S', | |
}, $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt32(string $data): int | |
{ | |
[1 => $int32] = \unpack('l', $data); | |
return $int32; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromInt32(int $data): string | |
{ | |
return \pack('l', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt32(string $data, Endianness $endianness = null): int | |
{ | |
[1 => $uint32] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'V', | |
Endianness::BIG => 'N', | |
default => 'L', | |
}, $data); | |
return $uint32; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt32(int $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'V', | |
Endianness::BIG => 'N', | |
default => 'L', | |
}, $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toInt64(string $data): int | |
{ | |
[1 => $int64] = \unpack('q', $data); | |
return $int64; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromInt64(int $data): string | |
{ | |
return \pack('q', $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toUInt64(string $data, Endianness $endianness = null): int | |
{ | |
[1 => $uint64] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'P', | |
Endianness::BIG => 'J', | |
default => 'Q', | |
}, $data); | |
return $uint64; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromUInt64(int $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'P', | |
Endianness::BIG => 'J', | |
default => 'Q', | |
}, $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toFloat32(string $data, Endianness $endianness = null): float | |
{ | |
[1 => $float] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'g', | |
Endianness::BIG => 'G', | |
default => 'f', | |
}, $data); | |
return $float; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromFloat32(float $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'g', | |
Endianness::BIG => 'G', | |
default => 'f', | |
}, $data); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function toFloat64(string $data, Endianness $endianness = null): float | |
{ | |
[1 => $double] = \unpack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'e', | |
Endianness::BIG => 'E', | |
default => 'd', | |
}, $data); | |
return $double; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function fromFloat64(float $data, Endianness $endianness = null): string | |
{ | |
return \pack(match ($endianness ?? $this->endianness) { | |
Endianness::LITTLE => 'e', | |
Endianness::BIG => 'E', | |
default => 'd', | |
}, $data); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"runner.bootstrap": "vendor/autoload.php", | |
"runner.php_config": [ | |
"opcache.enable=1", | |
"opcache.enable_cli=1", | |
"opcache.jit_buffer_size=128M" | |
], | |
"report.generators": { | |
"expression": { | |
"generator": "expression", | |
"break": [ | |
"subject" | |
], | |
"cols": [ | |
"benchmark", | |
"subject", | |
"set", | |
"mode", | |
"rstdev" | |
] | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Bic\Bin\Tests\Bench; | |
use Bic\Bin\Converter; | |
use Bic\Bin\NativeConverter; | |
use PhpBench\Attributes\BeforeMethods; | |
use PhpBench\Attributes\Iterations; | |
use PhpBench\Attributes\ParamProviders; | |
use PhpBench\Attributes\Revs; | |
use PhpBench\Attributes\Warmup; | |
#[BeforeMethods('boot')] | |
#[Revs(10), Iterations(10), Warmup(1)] | |
final class UnpackBench | |
{ | |
private array $samples = []; | |
public function boot(): void | |
{ | |
for ($i = 0; $i < 10; ++$i) { | |
$this->samples[] = \random_bytes(8); | |
} | |
} | |
public function converters(): iterable | |
{ | |
yield 'raw' => [new Converter()]; | |
yield 'pack' => [new NativeConverter()]; | |
} | |
#[ParamProviders('converters')] | |
public function benchInt8(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toInt8($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchUInt8(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toUInt8($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchInt16(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toInt16($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchUInt16(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toUInt16($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchInt32(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toInt32($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchUInt32(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toUInt32($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchInt64(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toInt64($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchUInt64(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toUInt64($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchFloat32(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toFloat32($byte); | |
} | |
} | |
#[ParamProviders('converters')] | |
public function benchFloat64(array $params): void | |
{ | |
foreach ($this->samples as $byte) { | |
$params[0]->toFloat64($byte); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment