Skip to content

Commit 8c51af0

Browse files
committed
Remove passing shared array buffers in one chunk.
Fix nChunks calculation - drastically improve memory usage. Increase min chunk size to 1<<15 (32k) - speed improvement on smaller circuits. Serial chunk processing - better mem usage. Linter fixes
1 parent ee037e6 commit 8c51af0

File tree

1 file changed

+52
-57
lines changed

1 file changed

+52
-57
lines changed

src/engine_multiexp.js

Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ export default function buildMultiexp(curve, groupName) {
2323

2424
let sGIn;
2525
let fnName;
26-
if (groupName == "G1") {
27-
if (inType == "affine") {
26+
if (groupName === "G1") {
27+
if (inType === "affine") {
2828
fnName = "g1m_multiexpAffine_chunk";
2929
sGIn = G.F.n8*2;
3030
} else {
3131
fnName = "g1m_multiexp_chunk";
3232
sGIn = G.F.n8*3;
3333
}
34-
} else if (groupName == "G2") {
35-
if (inType == "affine") {
34+
} else if (groupName === "G2") {
35+
if (inType === "affine") {
3636
fnName = "g2m_multiexpAffine_chunk";
3737
sGIn = G.F.n8*2;
3838
} else {
@@ -44,33 +44,30 @@ export default function buildMultiexp(curve, groupName) {
4444
}
4545
const nPoints = Math.floor(buffBases.byteLength / sGIn);
4646

47-
if (nPoints == 0) return G.zero;
47+
if (nPoints === 0) return G.zero;
4848
const sScalar = Math.floor(buffScalars.byteLength / nPoints);
49-
if( sScalar * nPoints != buffScalars.byteLength) {
49+
if( sScalar * nPoints !== buffScalars.byteLength) {
5050
throw new Error("Scalar size does not match");
5151
}
5252

5353
const bitChunkSize = pTSizes[log2(nPoints)];
5454
const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1;
5555

56-
console.log("buffBases len", buffBases.byteLength, "sGIn", sGIn, "nPoints", nPoints);
57-
console.log("buffScalars len", buffScalars.byteLength, "sScalar", sScalar);
58-
console.log("bitChunkSize", bitChunkSize, "nChunks", nChunks);
5956

6057
const opPromises = [];
6158
for (let i=0; i<nChunks; i++) {
6259
const task = [
63-
{cmd: "ALLOCSET", var: 0, buff: buffBases.buffer},
64-
{cmd: "ALLOCSET", var: 1, buff: buffScalars.buffer},
60+
{cmd: "ALLOCSET", var: 0, buff: buffBases},
61+
{cmd: "ALLOCSET", var: 1, buff: buffScalars},
6562
{cmd: "ALLOC", var: 2, len: G.F.n8*3},
6663
{cmd: "CALL", fnName: fnName, params: [
67-
{var: 0},
68-
{var: 1},
69-
{val: sScalar},
70-
{val: nPoints},
71-
{val: i*bitChunkSize},
72-
{val: Math.min(sScalar*8 - i*bitChunkSize, bitChunkSize)},
73-
{var: 2}
64+
{var: 0}, //pBases
65+
{var: 1}, // pScalars
66+
{val: sScalar}, // scalarSize
67+
{val: nPoints}, // nPoints
68+
{val: i*bitChunkSize}, // startBit
69+
{val: Math.min(sScalar*8 - i*bitChunkSize, bitChunkSize)}, // chunkSize
70+
{var: 2} // pr
7471
]},
7572
{cmd: "GET", out: 0, var: 2, len: G.F.n8*3}
7673
];
@@ -94,17 +91,17 @@ export default function buildMultiexp(curve, groupName) {
9491

9592
async function _multiExp(buffBases, buffScalars, inType, logger, logText) {
9693
const MAX_CHUNK_SIZE = 1 << 22;
97-
const MIN_CHUNK_SIZE = 1 << 10;
94+
const MIN_CHUNK_SIZE = 1 << 15;
9895
let sGIn;
9996

100-
if (groupName == "G1") {
101-
if (inType == "affine") {
97+
if (groupName === "G1") {
98+
if (inType === "affine") {
10299
sGIn = G.F.n8*2;
103100
} else {
104101
sGIn = G.F.n8*3;
105102
}
106-
} else if (groupName == "G2") {
107-
if (inType == "affine") {
103+
} else if (groupName === "G2") {
104+
if (inType === "affine") {
108105
sGIn = G.F.n8*2;
109106
} else {
110107
sGIn = G.F.n8*3;
@@ -114,50 +111,48 @@ export default function buildMultiexp(curve, groupName) {
114111
}
115112

116113
const nPoints = Math.floor(buffBases.byteLength / sGIn);
117-
if (nPoints == 0) return G.zero;
114+
if (nPoints === 0) return G.zero;
118115
const sScalar = Math.floor(buffScalars.byteLength / nPoints);
119-
if( sScalar * nPoints != buffScalars.byteLength) {
116+
if( sScalar * nPoints !== buffScalars.byteLength) {
120117
throw new Error("Scalar size does not match");
121118
}
122119

123120
console.log("buffBases.buffer instanceof SharedArrayBuffer", buffBases.buffer instanceof SharedArrayBuffer);
124121
console.log("buffScalars.buffer instanceof SharedArrayBuffer", buffScalars.buffer instanceof SharedArrayBuffer);
125122

123+
let result = [];
126124
const opPromises = [];
127-
if (buffBases.buffer
128-
&& (buffBases.buffer instanceof SharedArrayBuffer)
129-
&& buffScalars.buffer
130-
&& (buffScalars.buffer instanceof SharedArrayBuffer)
131-
)
132-
{
133-
// If we are working with SharedArrayBuffers, we can do it in one chunk because memory is shared between threads
134-
if (logger) logger.debug(`Multiexp start: ${logText}: ${nPoints}`);
135-
opPromises.push(_multiExpChunk(buffBases, buffScalars, inType, logger, logText).then( (r) => {
136-
if (logger) logger.debug(`Multiexp end: ${logText}: ${nPoints}`);
137-
return r;
138-
}));
139-
} else {
140-
const bitChunkSize = pTSizes[log2(nPoints)];
141-
const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1;
142-
143-
let chunkSize;
144-
chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks));
145-
if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE;
146-
if (chunkSize<MIN_CHUNK_SIZE) chunkSize = MIN_CHUNK_SIZE;
147-
148-
for (let i=0; i<nPoints; i += chunkSize) {
149-
if (logger) logger.debug(`Multiexp start: ${logText}: ${i}/${nPoints}`);
150-
const n = Math.min(nPoints - i, chunkSize);
151-
const buffBasesChunk = buffBases.slice(i*sGIn, (i+n)*sGIn);
152-
const buffScalarsChunk = buffScalars.slice(i*sScalar, (i+n)*sScalar);
153-
opPromises.push(_multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText).then( (r) => {
154-
if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`);
155-
return r;
156-
}));
157-
}
125+
const bitChunkSize = pTSizes[log2(nPoints)];
126+
let nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1;
127+
128+
if (groupName === "G2") {
129+
// G2 has bigger points, so we reduce chunk size to optimize memory usage
130+
nChunks *= 2;
158131
}
159132

160-
const result = await Promise.all(opPromises);
133+
let chunkSize;
134+
//chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks));
135+
chunkSize = Math.floor(nPoints / nChunks);
136+
if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE;
137+
if (chunkSize<MIN_CHUNK_SIZE) chunkSize = MIN_CHUNK_SIZE;
138+
139+
for (let i=0; i<nPoints; i += chunkSize) {
140+
if (logger) logger.debug(`Multiexp start: ${logText}: ${i}/${nPoints}`);
141+
const n = Math.min(nPoints - i, chunkSize);
142+
143+
const buffBasesChunk = buffBases.slice(i*sGIn, (i+n)*sGIn);
144+
const buffScalarsChunk = buffScalars.slice(i*sScalar, (i+n)*sScalar);
145+
146+
// opPromises.push(_multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText).then((r) => {
147+
// if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`);
148+
// return r;
149+
// }));
150+
const r = await _multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText);
151+
if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`);
152+
result.push(r);
153+
}
154+
155+
//result = await Promise.all(opPromises);
161156

162157
let res = G.zero;
163158
for (let i=result.length-1; i>=0; i--) {

0 commit comments

Comments
 (0)