Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/Base58Encoding.Benchmarks/EndToEndDecodeBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace Base58Encoding.Benchmarks;
public class EndToEndDecodeBenchmark
{
private string _sig64 = default!;
private ulong[] _transposed64 = default!;
private uint[][] _jagged64 = default!;

[GlobalSetup]
Expand All @@ -28,10 +27,9 @@ public void Setup()
Random.Shared.NextBytes(bytes);
_sig64 = Base58.Bitcoin.Encode(bytes);

// Production tables on this branch are transposed column-major ulong.
_transposed64 = Base58BitcoinTables.DecodeTable64;

// The production table is transposed column-major ulong.
// Rebuild the original jagged row-major layout: jagged[i][j] = transposed[j*IntermediateSz64 + i].
ReadOnlySpan<ulong> transposed = Base58BitcoinTables.DecodeTable64;
int rows = Base58BitcoinTables.IntermediateSz64;
int cols = Base58BitcoinTables.BinarySz64;
_jagged64 = new uint[rows][];
Expand All @@ -40,7 +38,7 @@ public void Setup()
_jagged64[i] = new uint[cols];
for (int j = 0; j < cols; j++)
{
_jagged64[i][j] = (uint)_transposed64[j * rows + i];
_jagged64[i][j] = (uint)transposed[j * rows + i];
}
}

Expand Down Expand Up @@ -110,11 +108,11 @@ private int DecodeFull64(ReadOnlySpan<char> encoded, Span<byte> destination, boo
Span<ulong> binary = stackalloc ulong[Base58BitcoinTables.BinarySz64];
if (useSimd)
{
var table = _transposed64;
ReadOnlySpan<ulong> table = Base58BitcoinTables.DecodeTable64;
int rows = Base58BitcoinTables.IntermediateSz64;
for (int j = 0; j < Base58BitcoinTables.BinarySz64; j++)
{
binary[j] = TensorDot(intermediate, table.AsSpan(j * rows, rows));
binary[j] = TensorDot(intermediate, table.Slice(j * rows, rows));
}
}
else
Expand Down
23 changes: 13 additions & 10 deletions src/Base58Encoding.Benchmarks/EndToEndEncodeBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ private interface IMatmulMode { }

private byte[] _data = default!;
private uint[][] _jagged = default!;
private ulong[] _rowMajor = default!;

[GlobalSetup]
public void Setup()
Expand All @@ -41,19 +40,20 @@ public void Setup()
_data = new byte[Size];
rng.NextBytes(_data);

// Scalar arm reads the production jagged tables directly; the vectorized arms read each
// encode row laid out contiguously as ulong. Row length is IntermediateSz - 1 (the matmul
// only touches columns 0..IntermediateSz-2, accumulating into intermediate[1..]).
_jagged = Size == 32 ? Base58BitcoinTables.EncodeTable32 : Base58BitcoinTables.EncodeTable64;
// The vectorized arms read the production row-major table, and the scalar arm a jagged copy of it.
// Row length is IntermediateSz - 1, because the matmul only touches columns 0..IntermediateSz-2.
// It accumulates into intermediate[1..].
int binarySz = Size == 32 ? Base58BitcoinTables.BinarySz32 : Base58BitcoinTables.BinarySz64;
int rowLen = (Size == 32 ? Base58BitcoinTables.IntermediateSz32 : Base58BitcoinTables.IntermediateSz64) - 1;

_rowMajor = new ulong[binarySz * rowLen];
ReadOnlySpan<ulong> rowMajor = RowMajor(Size);
_jagged = new uint[binarySz][];
for (int i = 0; i < binarySz; i++)
{
_jagged[i] = new uint[rowLen];
for (int j = 0; j < rowLen; j++)
{
_rowMajor[i * rowLen + j] = _jagged[i][j];
_jagged[i][j] = (uint)rowMajor[i * rowLen + j];
}
}

Expand Down Expand Up @@ -183,14 +183,14 @@ private void AccumulateRows<TMode>(
return;
}

ulong[] rowMajor = _rowMajor;
ReadOnlySpan<ulong> rowMajor = RowMajor(Size);
Span<ulong> target = intermediate.Slice(1, rowLen);

if (typeof(TMode) == typeof(TensorMode))
{
for (int i = startRow; i < endRow; i++)
{
ReadOnlySpan<ulong> row = rowMajor.AsSpan(i * rowLen, rowLen);
ReadOnlySpan<ulong> row = rowMajor.Slice(i * rowLen, rowLen);
// destination[k] = (row[k] * binary[i]) + target[k]; addend and destination are the
// same span (allowed: they begin at the same location).
TensorPrimitives.MultiplyAdd(row, (ulong)binary[i], target, target);
Expand All @@ -202,11 +202,14 @@ private void AccumulateRows<TMode>(
// SimdMode
for (int i = startRow; i < endRow; i++)
{
ReadOnlySpan<ulong> row = rowMajor.AsSpan(i * rowLen, rowLen);
ReadOnlySpan<ulong> row = rowMajor.Slice(i * rowLen, rowLen);
MultiplyAddSimd(row, binary[i], target);
}
}

private static ReadOnlySpan<ulong> RowMajor(int size) =>
size == 32 ? Base58BitcoinTables.EncodeTable32RowMajor : Base58BitcoinTables.EncodeTable64RowMajor;

// acc[k] += x[k] * scale, widest available width first, then a scalar tail / fallback.
private static void MultiplyAddSimd(ReadOnlySpan<ulong> x, ulong scale, Span<ulong> acc)
{
Expand Down
Loading
Loading