Skip to content

Commit 235bbd7

Browse files
authored
[protocol 3.6] Compression benchmarks (#2177)
1 parent a0639b1 commit 235bbd7

File tree

5 files changed

+147
-24
lines changed

5 files changed

+147
-24
lines changed

packages/loopring_v3.js/src/compression.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ export function decompress(data: string) {
7979

8080
export function compressLZ(
8181
input: string,
82+
minNumZeros: number = 42,
8283
speed: CompressionSpeed = CompressionSpeed.MEDIUM
8384
) {
84-
const minGasSavedForReplacement = GAS_COST_NONZERO_BYTE * 40;
85+
const minGasSavedForReplacement = GAS_COST_NONZERO_BYTE * minNumZeros;
8586
const minLengthReplacement = Math.floor(
8687
minGasSavedForReplacement / GAS_COST_NONZERO_BYTE
8788
);
@@ -262,10 +263,10 @@ export function decompressLZ(input: string) {
262263

263264
export function compressZeros(
264265
input: string,
266+
minNumZeros: Number = 42,
265267
speed: CompressionSpeed = CompressionSpeed.MEDIUM
266268
) {
267-
const minNumZeros = 20;
268-
const maxLength = 2**16-1;
269+
const maxLength = 2 ** 16 - 1;
269270

270271
const stream = new Bitstream(input);
271272
assert(stream.length() > 0, "cannot compress empty input");
@@ -283,22 +284,23 @@ export function compressZeros(
283284

284285
const writeLiterals = (numData: number, numZeros: number) => {
285286
// Make sure no length is longer than `maxLength`
286-
const numDataWriteLoops = Math.floor((numData + maxLength) / (maxLength + 1));
287-
for (let i = 0; i < numDataWriteLoops - 1; i++)
288-
{
289-
compressed.addNumber(maxLength, 2);
290-
compressed.addNumber(0, 2);
291-
for (let i = 0; i < maxLength; i++) {
292-
compressed.addNumber(data[previousPos + i], 1);
293-
}
294-
previousPos += maxLength;
295-
numData -= maxLength;
296-
}
297-
compressed.addNumber(numData, 2);
298-
compressed.addNumber(numZeros, 2);
299-
for (let i = 0; i < numData; i++) {
300-
compressed.addNumber(data[previousPos + i], 1);
287+
const numDataWriteLoops = Math.floor(
288+
(numData + maxLength) / (maxLength + 1)
289+
);
290+
for (let i = 0; i < numDataWriteLoops - 1; i++) {
291+
compressed.addNumber(maxLength, 2);
292+
compressed.addNumber(0, 2);
293+
for (let i = 0; i < maxLength; i++) {
294+
compressed.addNumber(data[previousPos + i], 1);
301295
}
296+
previousPos += maxLength;
297+
numData -= maxLength;
298+
}
299+
compressed.addNumber(numData, 2);
300+
compressed.addNumber(numZeros, 2);
301+
for (let i = 0; i < numData; i++) {
302+
compressed.addNumber(data[previousPos + i], 1);
303+
}
302304
};
303305

304306
while (pos < dataLength) {
@@ -357,4 +359,4 @@ export function decompressZeros(input: string) {
357359
}
358360
}
359361
return uncompressed.getData();
360-
}
362+
}

packages/loopring_v3/contracts/test/LzDecompressorContract.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,13 @@ contract LzDecompressorContract {
1414
{
1515
return LzDecompressor.decompress(data);
1616
}
17+
18+
function benchmark(
19+
bytes calldata data
20+
)
21+
external
22+
returns (bytes memory)
23+
{
24+
return LzDecompressor.decompress(data);
25+
}
1726
}

packages/loopring_v3/contracts/test/ZeroDecompressorContract.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,13 @@ contract ZeroDecompressorContract {
1414
{
1515
return ZeroDecompressor.decompress(data, 0);
1616
}
17+
18+
function benchmark(
19+
bytes calldata data
20+
)
21+
external
22+
returns (bytes memory)
23+
{
24+
return ZeroDecompressor.decompress(data, 0);
25+
}
1726
}

packages/loopring_v3/test/testCompression.ts

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
import { calculateCalldataCost, compressLZ, decompressLZ, compressZeros, decompressZeros, Bitstream } from "loopringV3.js";
1+
import {
2+
calculateCalldataCost,
3+
compressLZ,
4+
decompressLZ,
5+
compressZeros,
6+
decompressZeros,
7+
Bitstream
8+
} from "loopringV3.js";
9+
import { CompressionSpeed } from "loopringV3.js";
210

311
contract("Compression", (accounts: string[]) => {
4-
describe("LZ compression", () => {
12+
describe("LZ compression", function() {
13+
this.timeout(0);
14+
515
let lzDecompressor: any;
616

717
const compressLZChecked = (data: string) => {
@@ -41,10 +51,37 @@ contract("Compression", (accounts: string[]) => {
4151
};
4252

4353
before(async () => {
44-
const LzDecompressorContract = artifacts.require("LzDecompressorContract");
54+
const LzDecompressorContract = artifacts.require(
55+
"LzDecompressorContract"
56+
);
4557
lzDecompressor = await LzDecompressorContract.new();
4658
});
4759

60+
it.skip("Optimizer", async () => {
61+
const data =
62+
"0x0123456789987654301111111111111111111111111115548914444444444444121288412354425140000000000000" +
63+
"151156455787878787878787878787878454000000000000000000000000000000000000456487844878984567000000" +
64+
"151515151515151515151515151515151515151515151500000000000000000000000000000000000000000000000000" +
65+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
66+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
67+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001";
68+
69+
let bestGas = 10000000;
70+
let bestN = 0;
71+
for (let i = 20; i < 60; i++) {
72+
const compressed = compressLZ(data, i, CompressionSpeed.SLOW);
73+
console.log(compressed);
74+
const tx = await lzDecompressor.benchmark(compressed);
75+
const gasUsed = tx.receipt.gasUsed;
76+
if (gasUsed < bestGas) {
77+
bestGas = gasUsed;
78+
bestN = i;
79+
}
80+
console.log("" + i + ": " + gasUsed);
81+
}
82+
console.log("Best - " + bestN + ": " + bestGas);
83+
});
84+
4885
it("Test data", async () => {
4986
const data =
5087
"0x0123456789987654301111111111111111111111111115548914444444444444121288412354425140000000000000" +
@@ -150,7 +187,9 @@ contract("Compression", (accounts: string[]) => {
150187
};
151188

152189
before(async () => {
153-
const ZeroDecompressorContract = artifacts.require("ZeroDecompressorContract");
190+
const ZeroDecompressorContract = artifacts.require(
191+
"ZeroDecompressorContract"
192+
);
154193
zeroDecompressor = await ZeroDecompressorContract.new();
155194
});
156195

@@ -165,6 +204,30 @@ contract("Compression", (accounts: string[]) => {
165204
await compressAndDecompressZerosChecked(data);
166205
});
167206

207+
it.skip("Optimizer", async () => {
208+
const data =
209+
"0x0123456789987654301111111111111111111111111115548914444444444444121288412354425140000000000000" +
210+
"151156455787878787878787878787878454000000000000000000000000000000000000456487844878984567000000" +
211+
"151515151515151515151515151515151515151515151500000000000000000000000000000000000000000000000000" +
212+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
213+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
214+
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001";
215+
216+
let bestGas = 10000000;
217+
let bestN = 0;
218+
for (let i = 8; i < 100; i++) {
219+
const compressed = compressZeros(data, i);
220+
const tx = await zeroDecompressor.benchmark(compressed);
221+
const gasUsed = tx.receipt.gasUsed;
222+
if (gasUsed < bestGas) {
223+
bestGas = gasUsed;
224+
bestN = i;
225+
}
226+
console.log("" + i + ": " + gasUsed);
227+
}
228+
console.log("Best - " + bestN + ": " + bestGas);
229+
});
230+
168231
it("Random data", async () => {
169232
const numRounds = 8;
170233
const maxLength = 25 * 1000;
@@ -218,5 +281,4 @@ contract("Compression", (accounts: string[]) => {
218281
await compressAndDecompressZerosChecked(bitstream.getData());
219282
});
220283
});
221-
222284
});

packages/loopring_v3/test/testDebugTools.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AmmPool } from "./ammUtils";
44
import { Constants } from "loopringV3.js";
55
import { ExchangeTestUtil, OnchainBlock } from "./testExchangeUtil";
66
import { BlockCallback, GasTokenConfig } from "./types";
7+
import { calculateCalldataCost, compressZeros } from "loopringV3.js";
78

89
contract("Exchange", (accounts: string[]) => {
910
let ctx: ExchangeTestUtil;
@@ -20,6 +21,46 @@ contract("Exchange", (accounts: string[]) => {
2021
describe("Debug Tools", function() {
2122
this.timeout(0);
2223

24+
it.skip("submitBlocks tx data compressor", async () => {
25+
const data = "";
26+
27+
//console.log("original gas cost: " + calculateCalldataCost(data));
28+
29+
const decodedInput = web3.eth.abi.decodeParameters(
30+
[
31+
"bool",
32+
"bytes",
33+
{
34+
"struct CallbackConfig": {
35+
"struct BlockCallback[]": {
36+
blockIdx: "uint16",
37+
"struct TxCallback[]": {
38+
txIdx: "uint16",
39+
numTxs: "uint16",
40+
receiverIdx: "uint16",
41+
data: "bytes"
42+
}
43+
},
44+
receivers: "address[]"
45+
}
46+
}
47+
],
48+
"0x" + data.slice(2 + 4 * 2)
49+
);
50+
51+
const ctx = new ExchangeTestUtil();
52+
await ctx.initialize(accounts);
53+
54+
const encodedData = await ctx.getSubmitBlocksWithCallbacks({
55+
isDataCompressed: true,
56+
data: compressZeros(decodedInput[1]),
57+
callbackConfig: decodedInput[2]
58+
});
59+
60+
//console.log("new gas cost: " + calculateCalldataCost(encodedData));
61+
console.log(encodedData);
62+
});
63+
2364
it.skip("submitBlocks tx data", async () => {
2465
const blockDirectory = "./blocks/";
2566

0 commit comments

Comments
 (0)