From 432f30d88b06a238e0eaa461c751bde735ca9c27 Mon Sep 17 00:00:00 2001 From: Tuyen Nguyen Date: Mon, 15 Apr 2024 15:20:40 +0700 Subject: [PATCH] chore: documentation and benchmark in CI --- packages/as-sha256/package.json | 2 +- packages/as-sha256/src/index.ts | 56 ++++++++++++----------- packages/as-sha256/test/perf/simd.test.ts | 25 +++++++--- packages/as-sha256/test/unit/simd.test.ts | 4 +- 4 files changed, 51 insertions(+), 36 deletions(-) diff --git a/packages/as-sha256/package.json b/packages/as-sha256/package.json index b4d4aa9d..cb29c71c 100644 --- a/packages/as-sha256/package.json +++ b/packages/as-sha256/package.json @@ -38,7 +38,7 @@ "test:unit": "yarn run test:unit:node && yarn run test:unit:browser", "test:unit:node": "run -T mocha -r ts-node/register test/unit/*.test.ts", "test:unit:browser": "run -T karma start --single-run --browsers ChromeHeadless,FirefoxHeadless karma.config.js", - "benchmark": "node -r ts-node/register ./node_modules/.bin/benchmark 'test/perf/index.test.ts'", + "benchmark": "node -r ts-node/register ./node_modules/.bin/benchmark 'test/perf/*.test.ts'", "benchmark:local": "yarn benchmark --local", "test:ci": "yarn test:as-ci" }, diff --git a/packages/as-sha256/src/index.ts b/packages/as-sha256/src/index.ts index 96e7c58b..4951463b 100644 --- a/packages/as-sha256/src/index.ts +++ b/packages/as-sha256/src/index.ts @@ -18,9 +18,7 @@ export function digest(data: Uint8Array): Uint8Array { if (data.length <= ctx.INPUT_LENGTH) { inputUint8Array.set(data); ctx.digest(data.length); - const output = new Uint8Array(32); - output.set(outputUint8Array); - return output; + return outputUint8Array.slice(0, 32); } ctx.init(); @@ -32,9 +30,7 @@ export function digest64(data: Uint8Array): Uint8Array { if (data.length === 64) { inputUint8Array.set(data); ctx.digest64(wasmInputValue, wasmOutputValue); - const output = new Uint8Array(32); - output.set(outputUint8Array); - return output; + return outputUint8Array.slice(0, 32); } throw new Error("InvalidLengthForDigest64"); } @@ -44,9 +40,7 @@ export function digest2Bytes32(bytes1: Uint8Array, bytes2: Uint8Array): Uint8Arr inputUint8Array.set(bytes1); inputUint8Array.set(bytes2, 32); ctx.digest64(wasmInputValue, wasmOutputValue); - const output = new Uint8Array(32); - output.set(outputUint8Array); - return output; + return outputUint8Array.slice(0, 32); } throw new Error("InvalidLengthForDigest64"); } @@ -83,22 +77,30 @@ export function digest64HashObjects(obj1: HashObject, obj2: HashObject): HashObj } /** - * Hash 4 inputs, each 64 bytes - * @param i0 64 byte Uint8Array - * @param i1 - * @param i2 - * @param i3 + * Hash 4 Uint8Array objects in parallel, each 64 length as below + * Inputs: 0 1 2 3 4 5 6 7 + * \ / \ / \ / \ / + * Outputs: 0 1 2 3 */ -export function hash4Inputs(i0: Uint8Array, i1: Uint8Array, i2: Uint8Array, i3: Uint8Array): Uint8Array[] { - if (i0.length !== 64 || i1.length !== 64 || i2.length !== 64 || i3.length !== 64) { - throw new Error("Input length must be 64"); +export function hash4Input64s(inputs: Uint8Array[]): Uint8Array[] { + if (inputs.length !== 4) { + throw new Error("Input length must be 4"); + } + for (let i = 0; i < 4; i++) { + const input = inputs[i]; + if (input == null) { + throw new Error(`Input ${i} is null or undefined`); + } + if (input.length !== 64) { + throw new Error(`Invalid length ${input.length} at input ${i}`); + } } // set up input buffer for v128 - inputUint8Array.set(i0, 0); - inputUint8Array.set(i1, 64); - inputUint8Array.set(i2, 128); - inputUint8Array.set(i3, 192); + inputUint8Array.set(inputs[0], 0); + inputUint8Array.set(inputs[1], 64); + inputUint8Array.set(inputs[2], 128); + inputUint8Array.set(inputs[3], 192); ctx.hash4Inputs(wasmOutputValue); @@ -111,8 +113,12 @@ export function hash4Inputs(i0: Uint8Array, i1: Uint8Array, i2: Uint8Array, i3: } /** - * Hash 8 HashObjects: - * input${i} has h0 to h7, each 4 bytes which make it 32 bytes + * Hash 8 HashObjects in parallel: + * - input${i} has h0 to h7, each 4 bytes which make it 32 bytes + * + * Inputs h0 h1 h2 h3 h4 h5 h6 h7 + * \ / \ / \ / \ / + * Outputs o0 o1 o2 o3 */ export function hash8HashObjects(inputs: HashObject[]): HashObject[] { if (inputs.length !== 8) { @@ -243,7 +249,5 @@ function update(data: Uint8Array): void { function final(): Uint8Array { ctx.final(wasmOutputValue); - const output = new Uint8Array(32); - output.set(outputUint8Array); - return output; + return outputUint8Array.slice(0, 32); } diff --git a/packages/as-sha256/test/perf/simd.test.ts b/packages/as-sha256/test/perf/simd.test.ts index 051b5482..a9f08423 100644 --- a/packages/as-sha256/test/perf/simd.test.ts +++ b/packages/as-sha256/test/perf/simd.test.ts @@ -2,6 +2,14 @@ import { itBench, setBenchOpts } from "@dapplion/benchmark"; import * as sha256 from "../../src"; import {byteArrayToHashObject} from "../../src/hashObject"; +/** + * Mac M1 Apr 2024 + * + digest64 vs hash4Inputs vs hash8HashObjects + ✓ digest64 200092 times 6.816078 ops/s 146.7119 ms/op - 66 runs 10.3 s + ✓ hash 200092 times using hash4Input64s 8.093460 ops/s 123.5566 ms/op - 78 runs 10.2 s + ✓ hash 200092 times using hash8HashObjects 8.141334 ops/s 122.8300 ms/op - 78 runs 10.2 s + */ describe("digest64 vs hash4Inputs vs hash8HashObjects", function () { this.timeout(0); @@ -12,20 +20,23 @@ describe("digest64 vs hash4Inputs vs hash8HashObjects", function () { const input = Buffer.from("gajindergajindergajindergajindergajindergajindergajindergajinder", "utf8"); // total number of time running hash for 200000 balances const iterations = 50023; - itBench(`digest64 ${iterations} times`, () => { - for (let j = 0; j < iterations; j++) sha256.digest64(input); + itBench(`digest64 ${iterations * 4} times`, () => { + for (let j = 0; j < iterations * 4; j++) sha256.digest64(input); }); // hash4Inputs do 4 sha256 in parallel - const iterations2 = Math.floor(iterations / 4); - itBench(`hash ${iterations * 4} times using hash4Inputs`, () => { - for (let j = 0; j < iterations; j++) sha256.hash4Inputs(input, input, input, input); + itBench(`hash ${iterations * 4} times using hash4Input64s`, () => { + for (let j = 0; j < iterations; j++) { + sha256.hash4Input64s([input, input, input, input]); + } }); const hashObject = byteArrayToHashObject(Buffer.from("gajindergajindergajindergajinder", "utf8")); const hashInputs = Array.from({length: 8}, () => hashObject); // hash8HashObjects do 4 sha256 in parallel itBench(`hash ${iterations * 4} times using hash8HashObjects`, () => { - for (let j = 0; j < iterations; j++) sha256.hash8HashObjects(hashInputs); + for (let j = 0; j < iterations; j++) { + sha256.hash8HashObjects(hashInputs); + } }); -}); \ No newline at end of file +}); diff --git a/packages/as-sha256/test/unit/simd.test.ts b/packages/as-sha256/test/unit/simd.test.ts index 410f953d..bac0b5f6 100644 --- a/packages/as-sha256/test/unit/simd.test.ts +++ b/packages/as-sha256/test/unit/simd.test.ts @@ -8,7 +8,7 @@ describe("Test SIMD implementation of as-sha256", () => { const input1 = "gajindergajindergajindergajinder"; const input2 = "gajindergajindergajindergajinder"; const input = Buffer.from(input1 + input2, "utf8"); - const outputs = sha256.hash4Inputs(input, input, input, input); + const outputs = sha256.hash4Input64s([input, input, input, input]); const expectedOutput = new Uint8Array([ 190, 57, 56, 15, 241, 208, 38, 30, 111, 55, 218, 254, 66, 120, 182, 98, 239, 97, 31, 28, 178, 247, 192, 161, 131, 72, 178, 215, 235, 20, 207, 110, @@ -21,7 +21,7 @@ describe("Test SIMD implementation of as-sha256", () => { it("testHash4Inputs 1000 times", () => { for (let i = 0; i < 1000; i++) { const input = crypto.randomBytes(64); - const outputs = sha256.hash4Inputs(input, input, input, input); + const outputs = sha256.hash4Input64s([input, input, input, input]); const expectedOutput = sha256.digest64(input); expect(outputs[0]).to.be.deep.equal(expectedOutput); expect(outputs[1]).to.be.deep.equal(expectedOutput);