Skip to content

Commit

Permalink
Feat/rs webworker (#429)
Browse files Browse the repository at this point in the history
* chore: Update example, using reed solomon

* chore: Update Rollup config

* chore: Benchmark

* docs: Update README

* feat: Support WebWorker
  • Loading branch information
rrr523 authored Dec 19, 2023
1 parent cfb0941 commit afb5464
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-buttons-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@bnb-chain/reed-solomon': patch
---

feat: Support Web Worker
1 change: 1 addition & 0 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@bnb-chain/greenfield-cosmos-types": "0.4.0-alpha.25",
"@bnb-chain/greenfield-js-sdk": "workspace:*",
"@bnb-chain/greenfiled-file-handle": "workspace:*",
"@bnb-chain/reed-solomon": "workspace:*",
"@cosmjs/encoding": "^0.32.0",
"@cosmjs/proto-signing": "^0.32.0",
"@cosmjs/stargate": "^0.32.0",
Expand Down
13 changes: 5 additions & 8 deletions examples/nextjs/src/components/object/create/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { client } from '@/client';
import { ACCOUNT_PRIVATEKEY } from '@/config/env';
import { getOffchainAuthKeys } from '@/utils/offchainAuth';
import { ChangeEvent, useState } from 'react';
import { useAccount } from 'wagmi';
import { getCheckSumsWorker } from '@bnb-chain/greenfiled-file-handle';
import { ReedSolomon } from '@bnb-chain/reed-solomon';

export const CreateObject = () => {
const { address, connector } = useAccount();
Expand Down Expand Up @@ -64,14 +64,11 @@ export const CreateObject = () => {
const multiCal = await checksumWorker.generateCheckSumV2(file);
console.log('multiCal', multiCal);

const rs = new ReedSolomon();
const fileBytes = await file.arrayBuffer();
const hashResult = await (window as any).FileHandle.getCheckSums(
new Uint8Array(fileBytes),
);
const { contentLength, expectCheckSums } = hashResult;
const expectCheckSums = rs.encode(new Uint8Array(fileBytes));

console.log('offChainData', offChainData);
console.log('hashResult', hashResult);

const createObjectTx = await client.object.createObject(
{
Expand All @@ -81,8 +78,8 @@ export const CreateObject = () => {
visibility: 'VISIBILITY_TYPE_PRIVATE',
fileType: file.type,
redundancyType: 'REDUNDANCY_EC_TYPE',
contentLength,
expectCheckSums: JSON.parse(expectCheckSums),
contentLength: fileBytes.byteLength,
expectCheckSums: expectCheckSums,
// empty tags
// tags: {
// tags: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/reed-solomon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Use directly in the browser via script tag:
get reed solomon
</button>

<script src="https://cdn.jsdelivr.net/npm/@bnb-chain/reed-solomon/index.aio.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@bnb-chain/reed-solomon/dist/index.aio.js"></script>
<script>
const fileInput = document.getElementById('file');
Expand Down
19 changes: 19 additions & 0 deletions packages/reed-solomon/benchmark.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# benchmarks

## 20M

* Nodejs: 1s
* Nodejs(worker): 1s

## 200M

* Go: 300ms
* Nodejs: 10s
* Nodejs(worker): 2s
* Browser: 15s

## conclusion

When there are not many shards, using worker will not make performance faster.

Because worker communication is also a performance loss.
16 changes: 0 additions & 16 deletions packages/reed-solomon/examples/singlefile.js

This file was deleted.

57 changes: 57 additions & 0 deletions packages/reed-solomon/examples/web.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" id="file" />

<button id="btn">
get reed solomon
</button>

<button id="worker-btn">
get reed solomon (webworker)
</button>

<script src="../dist/index.aio.js"></script>
<script src="../dist/web.adapter.aio.js"></script>
<script src="../dist/utils.aio.js"></script>
<script type="module">
const fileInput = document.getElementById('file');

// not use webworker
document.getElementById('btn').onclick = async function() {
const selectFile = fileInput.files[0];
const arrBuffer = await selectFile.arrayBuffer()
if (!arrBuffer) alert('no file selected');

const sourceData = new Uint8Array(arrBuffer)
console.time('cost')
console.log('file size', sourceData.length / 1024 / 1024, 'm')
const rs = new RS.ReedSolomon()
const res = await rs.encode(sourceData)
console.log('res', res)
console.timeEnd('cost')
}

// use webworker
document.getElementById('worker-btn').onclick = async function() {
const selectFile = fileInput.files[0];
const arrBuffer = await selectFile.arrayBuffer()
if (!arrBuffer) alert('no file selected');

const sourceData = new Uint8Array(arrBuffer)
console.time('webworker cost')
console.log('file size', sourceData.length / 1024 / 1024, 'm')
const rs = new WebAdapter.WebAdapterReedSolomon()
const res = await rs.encodeInWorker(WebAdapter.injectWorker, sourceData)
console.log('res', res)
console.timeEnd('webworker cost')
}

</script>
</body>
</html>
17 changes: 16 additions & 1 deletion packages/reed-solomon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"types": "./types/index.d.ts",
"exports": {
".": {
"import": "./dist/index.aio.js",
"import": "./dist/index.js",
"require": "./dist/index.js",
"main": "./dist/index.js",
"default": "./dist/index.js",
Expand All @@ -19,11 +19,26 @@
"node": "./dist/node.adapter.js",
"require": "./dist/node.adapter.js"
},
"./web.adapter": {
"default": "./dist/web.adapter.js",
"types": "./types/web.adapter.d.ts",
"import": "./dist/web.adapter.js"
},
"./utils": {
"import": "./dist/utils.esm.js",
"require": "./dist/utils.js",
"main": "./dist/utils.js",
"default": "./dist/utils.js",
"types": "./types/utils.d.ts"
}
},
"typesVersions": {
"*": {
"node.adapter": ["./types/node.adapter.d.ts"],
"web.adapter": ["./types/web.adapter.d.ts"],
"utils": ["./types/utils.d.ts"]
}
},
"scripts": {
"prebuild": "rimraf ./dist",
"build": "rollup -c"
Expand Down
43 changes: 41 additions & 2 deletions packages/reed-solomon/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ function resolveExternal() {

export default async () => {
return [
// ESM
{
input: './src/index.js',
input: ['./src/index.js', './src/web.adapter.js'],
output: {
file: 'dist/index.esm.js',
format: 'es',
dir: 'dist',
entryFileNames: '[name].esm.js',
sourcemap: true,
},
external: resolveExternal(),
Expand All @@ -26,6 +29,8 @@ export default async () => {
}),
],
},

// CJS
{
input: ['./src/index.js', './src/node.adapter.js', './src/utils.js'],
output: {
Expand All @@ -43,6 +48,8 @@ export default async () => {
// }),
],
},

// UMD
{
input: './src/index.js',
output: {
Expand All @@ -59,5 +66,37 @@ export default async () => {
}),
],
},
{
input: './src/web.adapter.js',
output: {
format: 'umd',
file: 'dist/web.adapter.aio.js',
name: 'WebAdapter',
sourcemap: true,
},
plugins: [
commonjs(),
resolve({
browser: true,
preferBuiltins: false,
}),
],
},
{
input: './src/utils.js',
output: {
format: 'umd',
file: 'dist/utils.aio.js',
name: 'RSUtils',
sourcemap: true,
},
plugins: [
commonjs(),
resolve({
browser: true,
preferBuiltins: false,
}),
],
},
];
};
1 change: 1 addition & 0 deletions packages/reed-solomon/src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { encode } from '@ethersproject/base64';
export { sha256 } from 'ethereum-cryptography/sha256.js';

export function concat(a, b) {
let res = [];
Expand Down
100 changes: 100 additions & 0 deletions packages/reed-solomon/src/web.adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { ReedSolomon } from '.';
import { sha256, getIntegrityUint8Array, toBase64, splitPrice } from './utils';

export class WebAdapterReedSolomon extends ReedSolomon {
async encodeInWorker(workerFn, sourceData, { webAdapterUrl, utilsUrl }) {
const chunkList = splitPrice(sourceData, this.segmentSize);

const workers = [];

for (let i = 0; i < chunkList.length; i++) {
// const worker = new Worker('worker.js');
const worker = createWorker(workerFn, {
webAdapterUrl,
utilsUrl,
});
workers.push(worker);
worker.postMessage({
index: i,
chunk: chunkList[i],
});
}

const plist = workers.map(
(worker) =>
new Promise((resolve) => {
worker.onmessage = (e) => {
resolve(e.data);
};
}),
);

return Promise.all(plist).then((RES) => {
let hashList = [];
let segChecksumList = [];
let encodeDataHashList = new Array(this.totalShards);
for (let i = 0; i < encodeDataHashList.length; i++) {
encodeDataHashList[i] = [];
}

for (let i = 0; i < RES.length; i++) {
segChecksumList.push(RES[i].segChecksum);
}

for (let i = 0; i < chunkList.length; i++) {
for (let j = 0; j < encodeDataHashList.length; j++) {
encodeDataHashList[j][i] = RES[i].encodeDataHash[j];
}
}

hashList[0] = sha256(getIntegrityUint8Array(segChecksumList));

for (let i = 0; i < encodeDataHashList.length; i++) {
hashList[i + 1] = sha256(getIntegrityUint8Array(encodeDataHashList[i]));
}

const res = toBase64(hashList);

return res;
});
}
}

function createWorker(f, { webAdapterUrl, utilsUrl }) {
var blob = new Blob([
'(' + f.toString() + ')(' + `'${webAdapterUrl}'` + ',' + `'${utilsUrl}'` + ')',
]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
return worker;
}

// inject worker script
export function injectWorker(cdnsUrls) {
importScripts(
cdnsUrls.webAdapterUrl ||
'https://cdn.jsdelivr.net/npm/@bnb-chain/reed-solomon/dist/index.aio.js',
);
importScripts('https://cdn.jsdelivr.net/npm/@bnb-chain/reed-solomon/dist/utils.aio.js');

const rs = new WebAdapter.WebAdapterReedSolomon();

onmessage = function (event) {
const { index, chunk } = event.data;
const encodeShards = rs.encodeSegment(chunk);
let encodeDataHash = [];

for (let i = 0; i < encodeShards.length; i++) {
const priceHash = RSUtils.sha256(encodeShards[i]);
encodeDataHash.push(priceHash);
}

postMessage({
index,
segChecksum: RSUtils.sha256(chunk),
encodeDataHash,
});

self.close();
};
}
4 changes: 0 additions & 4 deletions packages/reed-solomon/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,3 @@ declare module '@bnb-chain/reed-solomon' {
encode(data: Uint8Arary): string[];
}
}

// declare class NodeAdapterReedSolomon {
// encodeInWorker(p: string, data: Uint8Array): Promise<string[]>;
// }
14 changes: 14 additions & 0 deletions packages/reed-solomon/types/web.adapter.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare module '@bnb-chain/reed-solomon/web.adapter' {
export class WebAdapterReedSolomon {
encodeInWorker(
workerFn: () => void,
data: Uint8Array,
cdnUrls: {
webAdapterUrl: string;
utilsUrl: string;
},
): Promise<string[]>;
}

export function injectWorker(cdnsUrls?: { webAdapterUrl: string; utilsUrl: string }): void;
}
Loading

0 comments on commit afb5464

Please sign in to comment.