由nodejs gzip压缩引起的性能实验测试

环境

系统 CPU 内存 硬盘
windows 10 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz (6核12线程) 32GB Pcie3.0 SSD
ubuntu 20.04 Intel(R) Xeon(R) Gold 6133 CPU @ 2.50GHz (4核) 4GB HDD

测试案例

rust 中文文档作为测试案例,共有170多个文件,总体积10M+,最大文件3M+(只有1个),最小文件1K, 使用 vitest 做基准测试。

实现代码

ts 复制代码
// src/gzip.ts

import fs from 'node:fs'
import fsp from 'node:fs/promises'
import { promisify } from 'node:util'
import zlib from 'node:zlib'
import PQueue from 'p-queue'

function gzipCompressSync(inputPath: string, outputPath: string) {
  fs.writeFileSync(outputPath, zlib.gzipSync(fs.readFileSync(inputPath)))
}

async function gzipCompressAsync(inputPath: string, outputPath: string): Promise<void> {
  const buffer = await fsp.readFile(inputPath)
  const data = await promisify(zlib.gzip)(buffer)
  await fsp.writeFile(outputPath, data)
}

function gzipCompressStream(inputPath: string, outputPath: string): Promise<void> {
  return new Promise<void>((resolve, reject) => {
    const readStream = fs.createReadStream(inputPath)
    const writeStream = fs.createWriteStream(outputPath)
    const gzip = zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION })

    readStream.pipe(gzip).pipe(writeStream).on('finish', resolve).on('error', reject)
  })
}

// 同步
export function fn1(files: string[]) {
  files.forEach(filePath => {
    gzipCompressSync(filePath, filePath + '.gz')
  })
}

// 异步
export async function fn2(files: string[]): Promise<void> {
  await Promise.all(files.map(filePath => gzipCompressAsync(filePath, filePath + '.gz')))
}

// 异步 + 并发控制
export async function fn3(files: string[]): Promise<void> {
  const queue = new PQueue({ concurrency: 4 })
  files.forEach(filePath => {
    queue.add(() => gzipCompressAsync(filePath, filePath + '.gz'))
  })
  await queue.onIdle()
}

// 异步流式
export async function fn4(files: string[]): Promise<void> {
  await Promise.all(files.map(filePath => gzipCompressStream(filePath, filePath + '.gz')))
}

// 异步流式 + 并发控制
export async function fn5(files: string[]): Promise<void> {
  const queue = new PQueue({ concurrency: 4 })
  files.forEach(filePath => {
    queue.add(() => gzipCompressStream(filePath, filePath + '.gz'))
  })
  await queue.onIdle()
}

测试代码

ts 复制代码
// gzip.bench.ts

import { bench, describe } from 'vitest'
import fg from 'fast-glob'
import { fn1, fn2, fn3, fn4, fn5 } from 'src/gzip'

const files = fg.sync(['trpl-zh-cn-gh-pages/**/*.(html|css|js|svg|txt|ttf|woff|woff2)'])

describe('gzip', () => {
  bench('同步', () => fn1(files))

  bench('异步', () => fn2(files))

  bench('异步 + 并发控制', () => fn3(files))

  bench('异步流式', () => fn4(files))

  bench('异步流式 + 并发控制', () => fn5(files))
})

测试结果

windows10

名称 hz min max mean p75 p99 p995 p999 rme
同步 3.2818 295.25 320.24 304.71 308.72 320.24 320.24 320.24 ±1.71%
异步 7.4271 128.40 144.22 134.64 139.24 144.22 144.22 144.22 ±2.75%
异步 + 并发控制 8.3062 115.85 136.76 120.39 120.50 136.76 136.76 136.76 ±3.79%
异步流式 2.7015 362.85 392.05 370.16 371.38 392.05 392.05 392.05 ±1.77%
异步流式 + 并发控制 2.7968 346.35 391.09 357.55 359.39 391.09 391.09 391.09 ±2.54%
bash 复制代码
 BENCH  Summary
 异步 + 并发控制 - bench/gzip.bench.ts > gzip
    1.12x faster than 异步
    2.53x faster than 同步
    2.97x faster than 异步流式 + 并发控制
    3.07x faster than 异步流式

说明:在windows10下增加 concurrency ,对性能提升不明显,甚至会起负作用,设为4时,效果已经不错

ubuntu 20.04

名称 hz min max mean p75 p99 p995 p999 rme
同步 2.5521 379.56 406.43 391.83 402.72 406.43 406.43 406.43 ±1.93%
异步 3.8680 239.57 279.61 258.53 267.82 279.61 279.61 279.61 ±3.36%
异步 + 并发控制 3.1925 287.61 350.28 313.24 319.58 350.28 350.28 350.28 ±4.17%
异步流式 1.2683 762.20 817.18 788.47 804.72 817.18 817.18 817.18 ±1.87%
异步流式 + 并发控制 1.2323 785.02 834.35 811.50 823.15 834.35 834.35 834.35 ±1.42%
bash 复制代码
BENCH  Summary   
异步 - bench/gzip.bench.ts > gzip
    1.21x faster than 异步 + 并发控制     
    1.52x faster than 同步     
    3.05x faster than 异步流式     
    3.14x faster than 异步流式 + 并发控制

说明:在ubuntu下增加 concurrency ,会使结果逐渐接近不做并发控制的方案,大约在32时,跟不做并发控制的结果趋于一致。看起来跟cpu核心数、libuv线程池大小都毫无关联,原因跟硬盘、cpu、系统可能都有关系。

相关推荐
JS_Love2 小时前
nodejs 手动实现 multipart/byteranges 分块下载
node.js
白一梓3 小时前
Node.js 流全解
node.js
门前云梦1 天前
ollama+open-webui本地部署自己的模型到d盘+两种open-webui部署方式(详细步骤+大量贴图)
前端·经验分享·笔记·语言模型·node.js·github·pip
Watermelo6171 天前
Web Worker:让前端飞起来的隐形引擎
前端·javascript·vue.js·数据挖掘·数据分析·node.js·es6
前端老鹰1 天前
Node.js 日志处理利器:pino 模块全面解析
后端·node.js
JiaLin_Denny1 天前
Node.js 版本兼容问题:minimatch@10.0.3和minio@7.0.28 冲突的解决
node.js·node安装包冲突
rogerogers1 天前
告别 Node 版本混乱!fnm 让你的开发环境丝滑起飞
javascript·node.js
NeverSettle_1 天前
AI SSE 技术文档
前端·node.js
妮妮喔妮2 天前
vue3+vite中使用path-to-regexp以及相关的报错问题
node.js
拾光拾趣录2 天前
老板突然要看“代码当量 KPI”
前端·node.js