Skip to content

skrevolve/libuv-thread-pool-size

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libuv UV_THREADPOOL_SIZE test

image

Nodejs libuv의 스레드 풀 크기로 인해 애플리케이션에 병목 현상이 발생하는 방법과 이를 수정하는 방법입니다.
libuv에 존재하는 I/O가 많은 애플리케이션의 경우 libuv 스레드 풀 크기는 심각한 병목 현상이 될 수 있으며 총 처리량을 늘리는데 가장 큰 영향을 미치는 요인중 하나가 될 수 있습니다.

What is libuv?

image
libuv는 비동기 I/O를 위한 다중 플랫폼 라이브러리입니다

Must Knows

  • Node.js의 I/O 방법 중 일부는 libuv에 의존합니다.
    가능한 경우 Node.js는 이미 비동기/비차단 API를 사용합니다.
    다른 경우 libuv는 스레드 풀을 사용하여 동기화/차단 I/O를 비동기/비차단으로 전환합니다
  • libuv의 스레드 풀 크기는 기본적으로 4 입니다

What’s the Problem?

libuv의 기본 스레드 풀 크기는 4입니다.
libv에 의존하는 4개 이상의 동시 I/O작업을 수행하면 각각의 추가 작업이 대기열에서 대기해야 하므로 libuv의 작은 스레드 풀 크기가 병목 현상을 발생시킵니다.

image

다음은 libuv의 thread pool을 사용하는 일부 I/O 작업입니다.

  • file system : fs.FSWatcher(), syncrhonus fs를 제외한 모든 파일 시스템 작업
  • DNS : dns.lookup(), dns.lookupService()
  • Crypto : crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill()..
  • Zlib : synchronous API 제외
  • ub_queue_work를 호출하는 Lib 및 사용자 코드

Solving It

Node.js 를 시작하기 전에 I/O호출을 수행하기 전에 UV_THREADPOOL_SIZE 변수 크기를 늘려 변경 해야 합니다.
libuv는 스레드 풀에 의존하는 I/O 메서드를 처음 호출할 때 스레드 풀을 인스턴스화 합니다.
일단 인스턴스화되면 스레드 풀 크기(UV_THREADPOOL_SIZE)에 대한 변경 사항은 적용되지 않습니다.

Examples

Node.js를 시작하기 전에 UV_THREADPOOL_SIZE를 변경하는 것이 좋습니다.
Node.js를 시작하기 전에 환경 변수를 설정하면 의도가 더 명확해지고 무언가 잘못된 가능성이 줄어듭니다.(포함된 라이브러리가 부작용으로 I/O를 호출함)

Via Bash

UV_THREADPOOL_SIZE=64 node ./out/main.js

Via Windows CLI

SET UV_THREADPOOL_SIZE=64 && node ./out/main.js

As first instruction in Node.js app: (not recommended)

'use strict'
process.env.UV_THREADPOOL_SIZE=64

How High Should I Set It?

설정하는 값은 경우에 따라 다릅니다.
cpu 코어 수로 예시로한 자료들이 많은데 libuv측 이슈에서 상관이 있는지에 대한 논쟁이 있었습니다.
-> Change default thread pool size from fixed 4 to number of logical cores #1578
CPU intensive한 작업은 thread pool에서 처리되므로 thread 수를 늘리면 그만큼 context switching 이 늘어나 보통 물리코어 수만큼 size 설정하기를 권장한다고 합니다.
이에 대한 논쟁은 아래 테스트 결과를 통해 알아 보면 되겠습니다.

  • 스레드 풀 크기는 최대 동시 I/O 작업의 정확한 양입니다
  • 너무 높게 설정하면 너무 낮게 설정하는 것보다 처리량에 미치는 영향이 적습니다
  • 극단적으로 높게 설정할 필요는 없습니다
  • 메모리 오버헤드는 128개 스레드에 대해 ~1MB 입니다
  • 최대값(1.30.0 이후)은 1024 입니다
  • CPU 코어 수로 설정하는 것은 좋은 기준이 아닙니다. 이는 CPU 집약적인 작업이 아닌 I/O 작업입니다

You can count threads in Linux

ps -Lef | grep "\<node\>" | wc -l

Run Server

You can change UV_THREADPOOL_SIZE option in package.json

Install & Build

npm install
npm run build

Node

npm run start:dev

PM2 fork mode

npm run start:fork

PM2 cluster mode

npm run start:cluster

Running test in CLI

chmod +x curl.sh #if you need change chown
./curl.sh -c 1 #run test case 1
./curl.sh -c 2 #run test case 1
./curl.sh -c 3 #run test case 1

💥Running test

case 1 (pm2 fork mode)

  • parallels : 1
  • curl request : 15
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 1 15 10 0.446 18.723 9.620
8 1 15 10 0.591 11.809 6.130
12 (my cpu length) 1 15 10 0.844 10.408 5.536
16 1 15 10 0.902 10.526 5.770
32 1 15 10 1.413 10.249 6.160
64 1 15 10 3.569 9.970 7.033
128 1 15 10 6.394 9.602 8.693
256 1 15 10 7.048 10.731 9.846
512 1 15 10 6.755 10.396 9.561
1024 1 15 10 7.233 10.691 9.732

case 2 (pm2 fork mode)

  • parallels : 15
  • curl request : 1
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 15 1 10 0.513 18.930 9.722
8 15 1 10 0.655 11.780 6.150
12 (my cpu length) 15 1 10 0.837 11.037 5.850
16 15 1 10 0.956 11.171 6.077
32 15 1 10 2.106 10.768 6.495
64 15 1 10 3.953 10.805 7.509
128 15 1 10 6.287 10.451 9.230
256 15 1 10 5.707 10.945 10.004
512 15 1 10 5.106 10.697 10.091
1024 15 1 10 4.726 10.976 9.933

case 3 (pm2 fork mode)

  • parallels : 15
  • curl request : 5
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 15 5 10 0.563 95.393 47.842
8 15 5 10 0.661 57.982 29.125
12 (my cpu length) 15 5 10 0.903 54.387 27.639
16 15 5 10 0.932 54.397 27.845
32 15 5 10 1.605 53.590 27.916
64 15 5 10 3.250 53.544 29.213
128 15 5 10 8.490 51.981 30.532
256 15 5 10 9.516 51.348 33.927
512 15 5 10 24.724 49.495 39.482
1024 15 5 10 12.087 53.127 48.663

case 1 (pm2 cluster mode :2 instances)

  • parallels : 1
  • curl request : 15
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 15 5 10 0.566 12.160 6.179
8 15 5 10 0.889 10.332 5.611
12 (my cpu length) 15 5 10 1.209 10.285 5.839
16 15 5 10 1.680 10.440 6.290
32 15 5 10 3.599 9.646 6.839
64 15 5 10 5.583 9.463 8.539
128 15 5 10 8.088 10.248 9.692
256 15 5 10 7.358 10.064 9.508
512 15 5 10 8.137 10.971 10.109
1024 15 5 10 7.433 10.660 9.789

case 2 (pm2 cluster mode :2 instances)

  • parallels : 15
  • curl request : 1
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 15 1 10 0.642 12.685 6.411
8 15 1 10 0.978 11.726 6.064
12 (my cpu length) 15 1 10 1.299 10.892 6.320
16 15 1 10 1.908 11.186 6.615
32 15 1 10 4.206 10.486 7.313
64 15 1 10 7.378 10.840 9.119
128 15 1 10 6.445 10.621 9.995
256 15 1 10 7.303 10.917 10.133
512 15 1 10 7.157 11.214 10.788
1024 15 1 10 9.703 11.351 10.843

case 3 (pm2 cluster mode :2 instances)

  • parallels : 15
  • curl request : 5
UV_THREADPOOL_SIZE parallels curl request I/O operations min(sec) max(sec) average(sec)
4 15 5 10 0.665 62.578 29.898
8 15 5 10 0.965 53.793 27.016
12 (my cpu length) 15 5 10 0.989 53.703 27.705
16 15 5 10 1.206 53.640 27.870
32 15 5 10 4.024 53.233 28.851
64 15 5 10 6.078 52.651 30.346
128 15 5 10 11.333 53.116 35.057
256 15 5 10 32.019 50.880 40.265
512 15 5 10 32.246 54.943 51.456
1024 15 5 10 34.931 53.018 49.394

참고자료