Hoher Speicherverbrauch bei Massen-OCR

This article was translated from English: Does it need improvement?
Translated
View the article in English

Das Ausführen von OCR über viele PDF-Segmente zugleich vervielfacht den Speicherverbrauch. Jede Aufgabe rendert vollseitige Bitmaps durch einen OcrInput, und eine neue IronTesseract Engine pro Segment lädt die Sprachmoduldateien jedes Mal neu. Bei voller Prozessor-Konkurrenz wird der maximale Speicherbedarf in den Multi-GB-Bereich gedrückt, mit Spitzen, die in speicherbeschränkten Umgebungen scheitern.

OCR ist von Natur aus speicherintensiv. Jeder OcrInput rendert vollseitige Bitmaps, und jede IronTesseract Engine lädt Sprachmoduldateien in den Speicher. Das Erstellen einer neuen Engine pro Segment lädt diese Modelle wiederholt neu, und das Ausführen einer OCR-Aufgabe pro CPU-Kern (Environment.ProcessorCount) erlaubt es, viele bitmap-lastige Jobs nebeneinander laufen zu lassen. Da es keine Begrenzung gibt, wie viele Aufgaben aktiv sind, skaliert der maximale Speicherbedarf direkt mit der Konkurrenz.

Die Lösung besteht darin, die Anzahl der gerade in Arbeit befindlichen Jobs zu begrenzen: Begrenzen Sie die Konkurrenz, wiederverwendbare Motoren aus einem Pool verwenden und Arbeiten mit einem Semaphor steuern.

Lösung

1. OCR-Konkurrenz begrenzen

Begrenzen Sie die Anzahl der gleichzeitigen OCR-Aufgaben auf eine kleine Obergrenze. Weniger gleichzeitige Aufgaben bedeuten weniger Vollseiten-Bitmaps im Speicher zugleich, was die Spitzenwerte direkt senkt. Passen Sie die Obergrenze an die Leistungsfähigkeit der Maschine an.

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

' Clamp concurrency to avoid memory saturation and CPU over-subscription.
Dim concurrency As Integer = Math.Clamp(Environment.ProcessorCount \ 2, 1, 4)
$vbLabelText   $csharpLabel

2. Die Motoren Poolen

Erstellen Sie genau eine IronTesseract Engine pro konkurrierendem Slot beim Start und verwenden Sie sie über jedes Segment hinweg erneut, anstatt jedes Mal eine neue Engine zu konstruieren und das Sprachmodell neu zu laden.

// Pre-create one engine per concurrent slot and reuse them across segments.
var enginePool = new ConcurrentBag<IronTesseract>(
    Enumerable.Range(0, concurrency).Select(_ => new 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())
);
' Pre-create one engine per concurrent slot and reuse them across segments.
Dim enginePool As New ConcurrentBag(Of IronTesseract)(
    Enumerable.Range(0, concurrency).Select(Function(_) New IronTesseract())
)
$vbLabelText   $csharpLabel

Das Bauen des Pools einmal amortisiert die Kosten für das Laden des Sprachmoduls über den gesamten Lauf statt pro Segment zu bezahlen.

3. Arbeiten mit einem Semaphor steuern

Initialisieren Sie ein SemaphoreSlim auf das Konkurrenzlimit und umwickeln Sie es mit using. Jede Aufgabe ruft WaitAsync() auf, bevor sie startet, und Release() in einem finally auf, sodass nur die erlaubte Anzahl von Segmenten gleichzeitig aktiv ist.

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();
}
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();
}
Imports System.Threading
Imports IronOcr

Dim semaphore As New SemaphoreSlim(concurrency)
Await semaphore.WaitAsync()
Try
    ' Rent a pre-loaded engine from the pool.
    Dim ocr As IronTesseract = Nothing
    If Not enginePool.TryTake(ocr) Then
        ocr = New IronTesseract() ' Defensive fallback; should never be reached.
    End If
    Try
        Using input As New OcrInput()
            input.LoadPdf(segmentStream) ' page-range segment produced upstream
            Dim ocrResult = Await ocr.ReadAsync(input)
            ocrResult.SaveAsSearchablePdf(outputPath)
        End Using
    Finally
        enginePool.Add(ocr) ' Return engine to pool for the next waiting segment.
    End Try
Finally
    semaphore.Release()
End Try
$vbLabelText   $csharpLabel

Der WaitAsync() Aufruf blockiert, bis ein Slot frei wird, und das Zurückgeben der Engine im inneren finally übergibt eine vorab geladene Engine direkt an das nächste wartende Segment.

4. Dispose OcrInput per segment

Wickeln Sie jede OcrInput in using ein, sodass die gerenderten Seiten-Bitmaps freigegeben werden, sobald das Segment gelesen wird, bevor die nächste Aufgabe den Slot beansprucht.

TippsDer using auf OcrInput verhindert, dass sich Bitmap-Speicher über Segmente hinweg ansammelt; ohne es würden freigegebene Slots immer noch ihre Seiten-Bitmaps halten.

Curtis Chau
Technischer Autor

Curtis Chau hat einen Bachelor-Abschluss in Informatik von der Carleton University und ist spezialisiert auf Frontend-Entwicklung mit Expertise in Node.js, TypeScript, JavaScript und React. Leidenschaftlich widmet er sich der Erstellung intuitiver und ästhetisch ansprechender Benutzerschnittstellen und arbeitet gerne mit modernen Frameworks sowie der Erstellung gut strukturierter, optisch ansprechender ...

Weiterlesen
Bereit anzufangen?
Nuget Downloads 6,136,090 | Version: 2026.7 gerade veröffentlicht
Still Scrolling Icon

Scrollst du immer noch?

Sie brauchen schnell einen Beweis? PM > Install-Package IronOcr
Führen Sie ein Beispiel aus und beobachten Sie, wie Ihr Bild zu durchsuchbarem Text wird.