バルクローバルOCR中のハイピークメモリー

Curtis Chau
Curtis Chau
Updated: 2026年6月29日

一度に多くのPDFセグメントに対してOCRを実行することはメモリー消費を倍増させます。 各タスクはIronTesseractエンジンが言語モデルファイルを毎回リロードします。フルプロセッサーの同時実行性では、これによりメモリピークが数GB範囲に達し、メモリに制限のある環境でのスパイクが失敗します。

OCRは生来メモリを多く使用します。 すべてのIronTesseractエンジンは言語モデルファイルをメモリーにロードします。 各セグメントごとに新しいエンジンを作成すると、それらのモデルが繰り返しリロードされ、CPUコアごとに1つのOCRタスクを実行すること(Environment.ProcessorCount)により、多くのビットマップヘビージョブが並行して実行されます。アクティブなタスクの制限がまったくないと、ピークメモリは完全に同時実行性に比例して拡大します。

修正方法は、飛行中のジョブの数を制限し: 同時実行性の制限を設定し、プールからエンジンを再利用し、セマフォで作業をゲートします。

解決策

1. OCRの同時実行性を制限します

同時に行うOCRタスクの数を小さな天井にクランプします。 少ない同時タスクは、一度にメモリーにあるフルページビットマップの数が減り、それがピークを直接下げます。 機械の能力に合わせて天井を調整します。

// Clamp concurrency to avoid memory saturation and CPU over-subscription.
int concurrency = Math.Clamp(Environment.ProcessorCount / 2, 1, 4);

2. エンジンをプールします

起動時に同時スロットごとに1つだけIronTesseractエンジンを作成し、それらを各セグメントで再利用する代わりに、毎回新しいエンジンを構築し、言語モデルを再ロードします。

// Pre-create one engine per concurrent slot and reuse them across segments.
var enginePool = new ConcurrentBag<IronTesseract>(
    Enumerable.Range(0, concurrency).Select(_ => new IronTesseract())
);

一度プールを構築することで、セグメントごとに支払うのではなく、全体の実行にわたって言語モデルのロードコストを分割します。

3. セマフォで作業をゲートします

同時実行の制限にusingでラップします。 各タスクは開始前にRelease()を呼び出し、許可された数のセグメントだけが一度に飛行中になります。

using var semaphore = new SemaphoreSlim(concurrency);
await semaphore.WaitAsync();
try
{
    // Rent a pre-loaded engine from the pool.
    if (!enginePool.TryTake(out var ocr))
        ocr = new IronTesseract(); // Defensive fallback; should never be reached.
    try
    {
        using var input = new OcrInput();
        input.LoadPdf(segmentStream); // page-range segment produced upstream
        var ocrResult = await ocr.ReadAsync(input);
        ocrResult.SaveAsSearchablePdf(outputPath);
    }
    finally
    {
        enginePool.Add(ocr); // Return engine to pool for the next waiting segment.
    }
}
finally
{
    semaphore.Release();
}

finallyにエンジンを戻すことで、事前ロードされたエンジンが次の待機中セグメントに直接渡されます。

4. 各セグメントごとにOcrInputを破棄します

各usingでラップして、そのセグメントが読み取られるとすぐにそのレンダードページビットマップが解放され、次のタスクがスロットを獲得する前に解放されます。

ヒント: セグメント間でビットマップのメモリが蓄積されないようにするのが、using の OcrInput です; それなしでは、解放されたスロットもページビットマップを保持します。

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

詳しく読む

準備はできましたか?

Nuget Downloads 6,357,159バージョン:2026.11リリースされたばかり