refactor(P6): Independent 배치 병렬화 + Magick ResourceLimits (즉효 최적화)
순차 for-loop(멀티코어를 1코어만 사용)을 코어 수 병렬로 — 50장 배치가 코어 수만큼 가속. ConvertOptions 불변(P4)이라 공유 읽기 스레드 안전. 시그니처 불변(내부 교체). - ConversionEngine.ConvertManyAsync: Independent 분기를 Parallel.ForEachAsync(MaxDOP=BatchParallelism)로. 결과는 인덱스 고정 ConvertResult[]로 순서 보존. 진행률은 완료 건수(Interlocked.Increment)로 보고 (파일별 분수 진행률은 동시 실행에서 의미가 흐려짐). DOP<=1이면 기존 순차 경로(세부 진행률 보존). CombineToSingle은 단일 연산이라 병렬 대상 아님(기존대로). - ConvertOptions.BatchParallelism(기본 = 논리 코어 수) 추가 — 미디어 위주 배치는 낮춰 튜닝 가능. - MagickProvider 정적 생성자: ResourceLimits.LimitMemory(60%) — 병렬 시 동시 MagickImage OOM 방지 (초과분 디스크 스필, 출력 바이트 무영향). - 신규 테스트: 8장 병렬 배치의 순서 보존(results[i]↔sources[i]) + 전수 변환 검증. 71개 테스트 전부 그린(골든마스터 동일성 유지), 빌드 0경고/0오류. ⚠ 진행률 UX(완료 건수 모델) 변경 — GUI 스모크 권장.
This commit is contained in:
parent
379a4a738d
commit
729b9b699d
4 changed files with 73 additions and 13 deletions
|
|
@ -52,22 +52,44 @@ public sealed class ConversionEngine
|
|||
return new[] { ConvertResult.Fail(sourceList[0], unsupportedReason ?? "단일 파일 결합을 지원하지 않습니다.") };
|
||||
}
|
||||
|
||||
var results = new List<ConvertResult>(sourceList.Count);
|
||||
for (var i = 0; i < sourceList.Count; i++)
|
||||
// 결과는 입력 인덱스로 고정해 병렬 실행에도 순서를 보존한다.
|
||||
var results = new ConvertResult[sourceList.Count];
|
||||
|
||||
// 독립(Independent) 배치는 코어 수만큼 병렬화 — ConvertOptions가 불변(P4)이라 공유 읽기가 안전하다.
|
||||
var dop = Math.Clamp(options.BatchParallelism, 1, sourceList.Count);
|
||||
|
||||
if (dop <= 1)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var source = sourceList[i];
|
||||
progress?.Report(new ConvertProgress(i, sourceList.Count, source, 0));
|
||||
|
||||
var result = await ConvertOneAsync(source, outputExtension, options,
|
||||
new Progress<double>(p => progress?.Report(new ConvertProgress(i, sourceList.Count, source, p))),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
results.Add(result);
|
||||
progress?.Report(new ConvertProgress(i + 1, sourceList.Count, source, 1));
|
||||
// 단일 파일/순차 경로 — 기존 동작과 동일(파일별 세부 진행률 보존).
|
||||
for (var i = 0; i < sourceList.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var source = sourceList[i];
|
||||
progress?.Report(new ConvertProgress(i, sourceList.Count, source, 0));
|
||||
results[i] = await ConvertOneAsync(source, outputExtension, options,
|
||||
new Progress<double>(p => progress?.Report(new ConvertProgress(i, sourceList.Count, source, p))),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
progress?.Report(new ConvertProgress(i + 1, sourceList.Count, source, 1));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// 병렬 경로 — 파일별 분수 진행률은 동시 실행에서 의미가 흐려지므로 완료 건수(Interlocked)로 보고한다.
|
||||
var completed = 0;
|
||||
var parallelOptions = new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = dop,
|
||||
CancellationToken = cancellationToken,
|
||||
};
|
||||
|
||||
await Parallel.ForEachAsync(Enumerable.Range(0, sourceList.Count), parallelOptions, async (i, ct) =>
|
||||
{
|
||||
var source = sourceList[i];
|
||||
results[i] = await ConvertOneAsync(source, outputExtension, options, null, ct).ConfigureAwait(false);
|
||||
var done = Interlocked.Increment(ref completed);
|
||||
progress?.Report(new ConvertProgress(done - 1, sourceList.Count, source, 1));
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ public sealed record ConvertOptions
|
|||
/// <summary>래스터화 같은 큰 손실 엣지를 회피한다.</summary>
|
||||
public bool AvoidLossy { get; init; } = false;
|
||||
|
||||
/// <summary>독립(Independent) 배치 변환의 최대 병렬 수. 기본 = 논리 코어 수. 미디어(FFmpeg) 위주 배치는 낮춰 오버서브스크립션 회피.</summary>
|
||||
public int BatchParallelism { get; init; } = Environment.ProcessorCount;
|
||||
|
||||
/// <summary>영상 인코딩 시 GPU 하드웨어 가속(NVENC)을 우선 시도하고, 실패하면 CPU로 자동 폴백한다.</summary>
|
||||
public bool VideoPreferGpu { get; init; } = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ public sealed class MagickProvider : IConverterProvider, IMultiInputConverter
|
|||
".pdf", ".tif", ".tiff", ".gif",
|
||||
};
|
||||
|
||||
static MagickProvider()
|
||||
{
|
||||
// 병렬 배치(P6)에서 동시 MagickImage가 늘어날 때 OOM/temp 스래싱을 막기 위해 메모리 상한 설정
|
||||
// (초과분은 디스크로 스필 — 출력 바이트에는 영향 없음). 환경에 따라 미지원이면 무시.
|
||||
try { ResourceLimits.LimitMemory(new Percentage(60)); } catch { }
|
||||
}
|
||||
|
||||
private static readonly string[] SingleFrameInputs =
|
||||
{
|
||||
".png", ".bmp", ".jpg", ".jpeg", ".jpe", ".webp", ".avif", ".psd",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue