由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、系统可能都有关系。

相关推荐
中国lanwp17 小时前
npm中@your-company:registry 和 registry 的区别
前端·npm·node.js
冴羽17 小时前
JavaScript 异步循环踩坑指南
前端·javascript·node.js
旧曲重听117 小时前
前端需要掌握多少Node.js?
前端·node.js
在掘金8011017 小时前
在Node.js中分析内存占用
node.js
abigale031 天前
开发实战 - ego商城 - 7 地址管理模块
前端·uni-app·node.js
码银1 天前
【简易聊天室】使用 HTML、CSS、JavaScript 结合 WebSocket 技术实现
javascript·css·node.js·html
桃子不吃李子1 天前
一些工具的使用
javascript·mongodb·node.js
五月君_1 天前
Node.js 24 LTS 正式发布,稳定支持到 2028 年
node.js
IT小哥哥呀1 天前
Node.js 实现企业内部消息通知系统(钉钉/企业微信机器人)
node.js·钉钉·企业微信·webhook·后端开发·自动化通知·mysql实战
-大头.1 天前
Node.js 环境变量配置全攻略
node.js·编辑器·vim