Skip to content

Instantly share code, notes, and snippets.

@SerafimArts
Created October 14, 2022 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SerafimArts/7d223310d55c330fa0da72f982ddc0f7 to your computer and use it in GitHub Desktop.
Save SerafimArts/7d223310d55c330fa0da72f982ddc0f7 to your computer and use it in GitHub Desktop.
bin pack/unpack
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% |
+-----------+------+---------+---------+
<?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);
}
}
<?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);
}
}
{
"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"
]
}
}
}
<?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