Wysokie zużycie pamięci podczas masowej operacji OCR

Curtis Chau
Curtis Chau
Updated: 29 czerwca 2026

Wykonywanie OCR na wielu segmentach PDF naraz zwielokrotnia zużycie pamięci. Każde zadanie renderuje pełnostronicowe mapy bitowe przez OcrInput, a nowy silnik IronTesseract na segment przeładowuje pliki modelu języka za każdym razem. Przy pełnej konkurencji procesorów powoduje to wzrost szczytowego zużycia pamięci do zakresu wielo-gigabajtowego, z przyrostami, które zawodzą w środowiskach z ograniczoną pamięcią.

OCR zużywa dużo pamięci z natury. Każdy OcrInput renderuje pełnostronicowe mapy bitowe, a każdy silnik IronTesseract ładuje pliki modelu języka do pamięci. Tworzenie nowego silnika dla kazdego przeladowania segmentu powoduje wielokrotne ladowanie tych modeli, a uruchamianie jednego zadania OCR na rdzen procesora (Environment.ProcessorCount) pozwala na uruchamianie wielu zadan z obszernymi bitmapami rownolegle. Bez ograniczen co do liczby aktywnych zadan, szczytowe uzycie pamieci rosnie bezposrednio wraz z rownoleglym przetwarzaniem.

Rozwiazanie polega na ograniczeniu liczby zadan w locie: ogranicz przetwarzanie rownolegle, ponownie wykorzystaj silniki z puli i kontroluj prace przy pomocy semafora.

Rozwiązanie

1. Ogranicz przetwarzanie OCR

Ogranicz liczbe jednoczesnych zadan OCR do niewielkiego poziomu. Mniej jednoczesnych zadan oznacza mniej bitmap pelnych stron w pamieci naraz, co bezposrednio obniza szczytowe uzycie pamieci. Dopasuj limit do mozliwosci maszyny.

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

2. Pula silnikow

Utworz dokladnie jeden silnik IronTesseract na kazdy rownolegly slot podczas uruchamiania i wykorzystaj je ponownie w kazdym segmencie, zamiast konstruowac nowy silnik i przeladowywac model jezykowy za kazdym razem.

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

Zbudowanie puli raz amortyzuje koszt przeladowania modelu jezykowego podczas calego uruchomienia zamiast placic za to przy kazdym segmencie.

3. Kontroluj prace przy pomocy semafora

Zainicjalizuj SemaphoreSlim do limitu rownoleglego przetwarzania i obejmij go using. Kazde zadanie wywoluje WaitAsync() zanim sie rozpocznie i Release() w finally, wiec tylko dozwolona liczba segmentow jest naraz uruchamiana.

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();
}

Wywolanie WaitAsync() blokuje, az zwolni sie slot, a zwrocenie silnika w wewnetrznym finally przekazuje w pelni przeladowany silnik bezposrednio do nastepnego oczekujacego segmentu.

4. Usuwanie OcrInput na segment

Obejmij kazdy OcrInput w using, aby jego bitmapy strony zostaly zwolnione w momencie odczytu segmentu, zanim nastepne zadanie zajmie slot.

Porady: using na OcrInput zapobiega gromadzeniu sie pamieci bitmap w segmentach; bez tego, zwolnione sloty nadal przechowuja bitmapy swoich stron.

Curtis Chau posiada tytuł licencjata z informatyki (Uniwersytet Carleton) i specjalizuje się w front-endowym rozwoju, z ekspertką w Node.js, TypeScript, JavaScript i React. Pasjonuje się tworzeniem intuicyjnych i estetycznie przyjemnych interfejsów użytkownika, Curtis cieszy się pracą z nowoczesnymi frameworkami i tworzeniem dobrze zorganizowanych, atrakcyjnych wizualnie podręczników.

Czytaj więcej

Gotowy, aby rozpocząć?

Nuget Downloads 6,357,159Wersja:2026.11właśnie wydany