1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00
denoland-deno/tests/unit_node/zlib_test.ts
Divy Srivastava 1ba88a7892
perf(ext/node): improve Buffer from string performance (#24567)
Fixes https://github.com/denoland/deno/issues/24323

- Use a Buffer pool for `fromString`
- Implement fast call base64 writes
- Direct from string `create` method for each encoding op

```
$ deno bench -A bench.mjs # 1.45.1+fee4d3a
cpu: Apple M1 Pro
runtime: deno 1.45.1+fee4d3a (aarch64-apple-darwin)

benchmark                time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------- -----------------------------
Buffer.from base64      550 ns/iter     (490 ns … 1'265 ns)    572 ns    606 ns  1'265 ns
Buffer#write base64     285 ns/iter       (259 ns … 371 ns)    307 ns    347 ns    360 ns

$ ~/gh/deno/target/release/deno bench -A bench.mjs # this PR
cpu: Apple M1 Pro
runtime: deno dev (aarch64-apple-darwin)

benchmark                time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------- -----------------------------
Buffer.from base64      151 ns/iter       (145 ns … 770 ns)    148 ns    184 ns    648 ns
Buffer#write base64   62.58 ns/iter     (60.79 ns … 157 ns)  61.65 ns  75.79 ns    141 ns

$ node bench.mjs # v22.4.0
cpu: Apple M1 Pro
runtime: node v22.4.0 (arm64-darwin)

benchmark                time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------- -----------------------------
Buffer.from base64      163 ns/iter     (96.92 ns … 375 ns)  99.45 ns    127 ns    220 ns
Buffer#write base64   75.48 ns/iter     (74.97 ns … 134 ns)  75.17 ns  81.83 ns  96.84 ns
```
2024-07-30 18:09:55 +05:30

183 lines
5.2 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "@std/assert";
import { fromFileUrl, relative } from "@std/path";
import {
brotliCompress,
brotliCompressSync,
brotliDecompressSync,
createBrotliCompress,
createBrotliDecompress,
createDeflate,
gzipSync,
unzipSync,
} from "node:zlib";
import { Buffer } from "node:buffer";
import { createReadStream, createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { buffer } from "node:stream/consumers";
Deno.test("brotli compression sync", () => {
const buf = Buffer.from("hello world");
const compressed = brotliCompressSync(buf);
const decompressed = brotliDecompressSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test("brotli compression async", async () => {
const buf = Buffer.from("hello world");
const compressed: Buffer = await new Promise((resolve) =>
brotliCompress(buf, (_, res) => {
return resolve(res);
})
);
assertEquals(compressed instanceof Buffer, true);
const decompressed = brotliDecompressSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test("gzip compression sync", () => {
const buf = Buffer.from("hello world");
const compressed = gzipSync(buf);
const decompressed = unzipSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test("brotli compression", {
ignore: true,
}, async () => {
const promise = Promise.withResolvers<void>();
const compress = createBrotliCompress();
const filePath = relative(
Deno.cwd(),
fromFileUrl(new URL("./testdata/lorem_ipsum.txt", import.meta.url)),
);
const input = createReadStream(filePath);
const output = createWriteStream("lorem_ipsum.txt.br");
const stream = input.pipe(compress).pipe(output);
stream.on("finish", () => {
const decompress = createBrotliDecompress();
const input2 = createReadStream("lorem_ipsum.txt.br");
const output2 = createWriteStream("lorem_ipsum.txt");
const stream2 = input2.pipe(decompress).pipe(output2);
stream2.on("close", () => promise.resolve());
});
await Promise.all([
promise.promise,
new Promise((r) => stream.on("close", r)),
]);
const content = Deno.readTextFileSync("lorem_ipsum.txt");
assert(content.startsWith("Lorem ipsum dolor sit amet"), content);
try {
Deno.removeSync("lorem_ipsum.txt.br");
} catch {
// pass
}
try {
Deno.removeSync("lorem_ipsum.txt");
} catch {
// pass
}
});
Deno.test("brotli end-to-end with 4097 bytes", () => {
const a = "a".repeat(4097);
const compressed = brotliCompressSync(a);
const decompressed = brotliDecompressSync(compressed);
assertEquals(decompressed.toString(), a);
});
Deno.test(
"zlib create deflate with dictionary",
async () => {
const { promise, resolve } = Promise.withResolvers<void>();
const handle = createDeflate({
dictionary: Buffer.alloc(0),
});
handle.on("close", () => resolve());
handle.end();
handle.destroy();
await promise;
},
);
Deno.test(
"zlib flush i32",
function () {
const handle = createDeflate({
// @ts-expect-error: passing non-int flush value
flush: "",
});
handle.end();
handle.destroy();
},
);
Deno.test("should work with dataview", () => {
const buf = Buffer.from("hello world");
const compressed = brotliCompressSync(
new DataView(buf.buffer, buf.byteOffset, buf.byteLength),
);
const decompressed = brotliDecompressSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test("should work with a buffer from an encoded string", () => {
const encoder = new TextEncoder();
const buffer = encoder.encode("hello world");
const buf = Buffer.from(buffer);
const compressed = brotliCompressSync(buf);
const decompressed = brotliDecompressSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test(
"zlib compression with dataview",
() => {
const buf = Buffer.from("hello world");
const compressed = gzipSync(
new DataView(buf.buffer, buf.byteOffset, buf.byteLength),
);
const decompressed = unzipSync(compressed);
assertEquals(decompressed.toString(), "hello world");
},
);
Deno.test("zlib compression with an encoded string", () => {
const encoder = new TextEncoder();
const buffer = encoder.encode("hello world");
const compressed = gzipSync(buffer);
const decompressed = unzipSync(compressed);
assertEquals(decompressed.toString(), "hello world");
});
Deno.test("brotli large chunk size", async () => {
const input = new Uint8Array(1000000);
for (let i = 0; i < input.length; i++) {
input[i] = Math.random() * 256;
}
const output = await buffer(
Readable.from([input])
.pipe(createBrotliCompress())
.pipe(createBrotliDecompress()),
);
assertEquals(output.length, input.length);
});
Deno.test("brotli decompress flush restore size", async () => {
const input = new Uint8Array(1000000);
const output = await buffer(
Readable.from([input])
.pipe(createBrotliCompress())
.pipe(createBrotliDecompress()),
);
assertEquals(output.length, input.length);
});