From bb43e8b125b9fdf6fb03d52b95b25f0d05ed71f7 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Wed, 13 May 2026 00:21:39 +0900 Subject: [PATCH] feat(installer-rp): auto-tune music concurrency to CPU core count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit os.cpus().length 기준 동시 다운로드 수를 자동 결정: - 2 코어 이하 → 2 동시 - 3~4 코어 → 3 동시 - 5~8 코어 → 4 동시 - 9 코어 이상 → 5 동시 (YouTube throttle 때문에 상한) 환경변수 MUSIC_CONCURRENCY 로 강제 오버라이드 가능(상한 8). 설치 로그에 감지된 코어 수와 선택된 동시성 노출. Co-Authored-By: Claude Opus 4.7 --- src/installer-rp/main.ts | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/installer-rp/main.ts b/src/installer-rp/main.ts index 72aaeb9..a2767e0 100644 --- a/src/installer-rp/main.ts +++ b/src/installer-rp/main.ts @@ -4,6 +4,7 @@ import https from 'node:https' import path from 'node:path' import fs from 'node:fs' import fsp from 'node:fs/promises' +import os from 'node:os' import { URL } from 'node:url' import type { ChildProcess } from 'node:child_process' import type { Manifest, PackDefinition, PackList } from '../shared/types.js' @@ -27,8 +28,23 @@ interface RpInstallerState { activeChildren: Set } -/** 동시 yt-dlp 프로세스 수. 너무 높이면 유튜브가 throttle. */ -const MUSIC_CONCURRENCY = 3 +/** + * 동시 yt-dlp 프로세스 수를 CPU 코어 수로 자동 결정. + * - yt-dlp + ffmpeg 변환이 CPU 바운드라 코어 수가 가장 좋은 프록시. + * - 유튜브가 IP 단위로 throttle 걸기 때문에 5 이상은 효과 없음 → 상한 5. + * - 환경변수 MUSIC_CONCURRENCY 로 강제 오버라이드 가능. + */ +function pickMusicConcurrency(): number { + const override = Number(process.env.MUSIC_CONCURRENCY) + if (Number.isFinite(override) && override >= 1) { + return Math.min(8, Math.floor(override)) + } + const cores = os.cpus()?.length ?? 4 + if (cores <= 2) return 2 + if (cores <= 4) return 3 + if (cores <= 8) return 4 + return 5 +} const DEFAULT_MANIFEST_URL = process.env.MANIFEST_URL ?? 'http://127.0.0.1:3000/manifest.json' @@ -199,10 +215,13 @@ ipcMain.handle('rp:install:start', async (): Promise<{ resourcepackPath: string sendProgress({ phase: 'prep', message: '준비 완료', done: true }) throwIfCancelled() - // 2-2. 음악 다운로드 (MUSIC_CONCURRENCY 개씩 병렬, ogg 변환) + // 2-2. 음악 다운로드 (CPU 코어 수 기반 자동 동시 다운로드, ogg 변환) const musicDir = path.join(tempRoot, 'music') await fsp.mkdir(musicDir, { recursive: true }) - sendLog(`음악 다운로드 시작 (${musicTotal}곡, 동시 ${MUSIC_CONCURRENCY}개)`) + const concurrency = pickMusicConcurrency() + const cpuCount = os.cpus()?.length ?? 0 + sendLog(`CPU 코어 ${cpuCount}개 감지 → 동시 다운로드 ${concurrency}개`) + sendLog(`음악 다운로드 시작 (${musicTotal}곡, 동시 ${concurrency}개)`) // 클로저 안에서 narrowing 이 풀리지 않도록 로컬 alias. const musicList = pack.list.music @@ -252,7 +271,7 @@ ipcMain.handle('rp:install:start', async (): Promise<{ resourcepackPath: string } } - const workerCount = Math.min(MUSIC_CONCURRENCY, musicTotal) + const workerCount = Math.min(concurrency, musicTotal) const workers: Promise[] = [] for (let w = 0; w < workerCount; w++) workers.push(musicWorker()) await Promise.all(workers)