Bitcast demo
This file contains 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
using System; | |
using System.Runtime.InteropServices; | |
public static class BenchDecode | |
{ | |
const int TRANSFER = 128; | |
[StructLayout(LayoutKind.Sequential, Size = 16)] | |
unsafe struct UInt128 | |
{ | |
public fixed byte bytes[16]; | |
} | |
[StructLayout(LayoutKind.Sequential, Size = TRANSFER)] | |
unsafe struct Transfer | |
{ | |
public UInt128 id; | |
public UInt128 debit_account_id; | |
public UInt128 credit_account_id; | |
public UInt128 user_data; | |
public UInt128 reserved; | |
public UInt128 pending_id; | |
public ulong timeout; | |
public uint ledger; | |
public ushort code; | |
public ushort flags; | |
public ulong amount; | |
public ulong timestamp; | |
} | |
public static void Main() { | |
var buffer = Load("transfers"); | |
Console.WriteLine("do this a few times to let CLR optimize..."); | |
int loops = 10; | |
while (loops-- > 0) | |
{ | |
int offset = 0; | |
ulong sum = 0UL; | |
var now = DateTime.Now; | |
while (offset < buffer.Length) | |
{ | |
var array = buffer[offset..][0..TRANSFER]; | |
// Deserialize without much overhead: | |
var transfer = MemoryMarshal.AsRef<Transfer>(array); | |
sum += transfer.amount; | |
offset += TRANSFER; | |
} | |
TimeSpan elapsed = DateTime.Now - now; | |
Console.WriteLine(" C#: sum of transfer amounts={0} ms={1:0.000}", sum, elapsed.TotalMilliseconds); | |
} | |
} | |
static Span<byte> Load(string file) | |
{ | |
return System.IO.File.ReadAllBytes(file).AsSpan(0); | |
} | |
} |
This file contains 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
package main | |
import ( | |
"fmt" | |
"os" | |
"time" | |
"unsafe" | |
) | |
type Uint128 struct { | |
data [16]byte | |
} | |
type Transfer struct { | |
id Uint128 | |
debit_account_id Uint128 | |
credit_account_id Uint128 | |
user_data Uint128 | |
reserved Uint128 | |
pending_iD Uint128 | |
timeout uint64 | |
ledger uint32 | |
code uint16 | |
flags uint16 | |
amount uint64 | |
timestamp uint64 | |
} | |
const TRANSFER int = 128 | |
func main() { | |
buffer := load("transfers") | |
var loops int = 10 | |
for loops > 0 { | |
var offset int = 0 | |
var sum uint64 = 0 | |
now := time.Now() | |
for offset < len(buffer) { | |
// Deserialize without any overhead: | |
transfer := (*Transfer)(unsafe.Pointer(&buffer[offset])) | |
sum += transfer.amount | |
offset += TRANSFER | |
} | |
elapsed := time.Since(now) | |
fmt.Printf(" go: sum of transfer amounts=%d ns=%d\n", sum, elapsed.Nanoseconds()) | |
loops -= 1 | |
} | |
} | |
func load(name string) []byte { | |
file, openErr := os.Open(name) | |
if openErr != nil { | |
panic(openErr) | |
} | |
defer file.Close() | |
stats, statsErr := file.Stat() | |
if statsErr != nil { | |
panic(statsErr) | |
} | |
var size int64 = stats.Size() | |
bytes := make([]byte, size) | |
_, readErr := file.Read(bytes) | |
if readErr != nil { | |
panic(readErr) | |
} | |
return bytes | |
} |
This file contains 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
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
import java.nio.ByteOrder; | |
import java.util.concurrent.TimeUnit; | |
public class BenchDecode { | |
static final int TRANSFER = 128; | |
static final int TRANSFER_ID = 16; | |
static final int TRANSFER_DEBIT_ID = 16; | |
static final int TRANSFER_CREDIT_ID = 16; | |
static final int TRANSFER_USER_DATA = 16; | |
static final int TRANSFER_RESERVED = 16; | |
static final int TRANSFER_PENDING_ID = 16; | |
static final int TRANSFER_TIMEOUT = 8; | |
static final int TRANSFER_LEDGER = 4; | |
static final int TRANSFER_CODE = 2; | |
static final int TRANSFER_FLAGS = 2; | |
static final int TRANSFER_AMOUNT = 8; | |
static final int TRANSFER_TIMESTAMP = 8; | |
static final int TRANSFER_ID_OFFSET = 0; | |
static final int TRANSFER_DEBIT_ID_OFFSET = 0 + 16; | |
static final int TRANSFER_CREDIT_ID_OFFSET = 0 + 16 + 16; | |
static final int TRANSFER_USER_DATA_OFFSET = 0 + 16 + 16 + 16; | |
static final int TRANSFER_RESERVED_OFFSET = 0 + 16 + 16 + 16 + 16; | |
static final int TRANSFER_PENDING_ID_OFFSET = 0 + 16 + 16 + 16 + 16 + 16; | |
static final int TRANSFER_TIMEOUT_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16; | |
static final int TRANSFER_LEDGER_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16 + 8; | |
static final int TRANSFER_CODE_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16 + 8 + 4; | |
static final int TRANSFER_FLAGS_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16 + 8 + 4 + 2; | |
static final int TRANSFER_AMOUNT_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16 + 8 + 4 + 2 + 2; | |
static final int TRANSFER_TIMESTAMP_OFFSET = 0 + 16 + 16 + 16 + 16 + 16 + 16 + 8 + 4 + 2 + 2 + 8; | |
public static void main(String[] args) { | |
final var buffer = load("transfers"); | |
System.out.println("do this a few times to let JVM optimize..."); | |
int loops = 10; | |
while (loops-- > 0) { | |
final var now = System.nanoTime(); | |
long sum = 0; | |
var offset = 0; | |
while (offset < buffer.capacity()) { | |
final var id_lsb = buffer.getLong(offset + TRANSFER_ID_OFFSET); | |
final var id_msb = buffer.getLong(offset + TRANSFER_ID_OFFSET + 8); | |
final var credit_id_lsb = buffer.getLong(offset + TRANSFER_CREDIT_ID_OFFSET); | |
final var credit_id_msb = buffer.getLong(offset + TRANSFER_CREDIT_ID_OFFSET + 8); | |
final var debit_id_lsb = buffer.getLong(offset + TRANSFER_DEBIT_ID_OFFSET); | |
final var debit_id_msb = buffer.getLong(offset + TRANSFER_DEBIT_ID_OFFSET + 8); | |
final var user_data_lsb = buffer.getLong(offset + TRANSFER_USER_DATA_OFFSET); | |
final var user_data_msb = buffer.getLong(offset + TRANSFER_USER_DATA_OFFSET + 8); | |
final var reserved_lsb = buffer.getLong(offset + TRANSFER_RESERVED_OFFSET); | |
final var reserved_msb = buffer.getLong(offset + TRANSFER_RESERVED_OFFSET + 8); | |
final var pending_lsb = buffer.getLong(offset + TRANSFER_PENDING_ID_OFFSET); | |
final var pending_msb = buffer.getLong(offset + TRANSFER_PENDING_ID_OFFSET + 8); | |
final var timeout = buffer.getLong(offset + TRANSFER_TIMEOUT_OFFSET); | |
final var ledger = buffer.getInt(offset + TRANSFER_LEDGER_OFFSET); | |
final var code = buffer.getShort(offset + TRANSFER_CODE_OFFSET); | |
final var flags = buffer.getShort(offset + TRANSFER_FLAGS_OFFSET); | |
final var amount = buffer.getLong(offset + TRANSFER_AMOUNT_OFFSET); | |
final var timestamp = buffer.getLong(offset + TRANSFER_TIMESTAMP_OFFSET); | |
sum += amount; | |
offset += TRANSFER; | |
} | |
final double elapsed_ms = (System.nanoTime() - now) / (double)TimeUnit.MILLISECONDS.toNanos(1); | |
System.out.printf("java: sum of transfer amounts=%s ms=%.3f%n", sum, elapsed_ms); | |
} | |
} | |
private static ByteBuffer load(String file) { | |
try (final var stream = new FileInputStream(file)) { | |
return ByteBuffer.wrap(stream.readAllBytes()).order(ByteOrder.nativeOrder()).position(0); | |
} catch (IOException exception) { | |
exception.printStackTrace(); | |
System.exit(-1); | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Deserializing 16,384 transfers on each language:
JS and Zig demos
https://github.com/tigerbeetledb/tigerbeetle/tree/main/demos/bitcast
Closed PR
tigerbeetle/tigerbeetle#195