Alta Memória de Pico Durante OCR em Massa

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

Executar OCR em muitos segmentos de PDF de uma vez multiplica o uso de memória. Cada tarefa renderiza bitmaps de página inteira através de um OcrInput, e um motor IronTesseract novo por segmento recarrega os arquivos do modelo de linguagem toda vez. Na plena concorrência do processador isso eleva a memória de pico para a faixa de vários GB, com picos que falham em ambientes com memória limitada.

O OCR é naturalmente pesado em memória. Cada OcrInput renderiza bitmaps de página inteira, e cada motor IronTesseract carrega arquivos do modelo de linguagem na memória. Criar um novo motor por segmento recarrega esses modelos repetidamente, e executar uma tarefa de OCR por núcleo de CPU (Environment.ProcessorCount) permite que muitos trabalhos pesados em bitmap sejam executados lado a lado. Sem nada limitando quantas tarefas estão ativas, a memória de pico escala diretamente com a concorrência.

A solução é limitar o número de tarefas em andamento: limitar a concorrência, reutilizar motores de um pool e organizar o trabalho com um semáforo.

Solução

1. Limitar a concorrência do OCR

Limite o número de tarefas de OCR simultâneas a um teto pequeno. Menos tarefas simultâneas significam menos bitmaps de página inteira na memória de uma vez, o que reduz diretamente o pico. Ajuste o teto à capacidade da máquina.

// 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. Pool dos motores

Crie exatamente um motor IronTesseract por slot concorrente na inicialização e reutilize-os em todos os segmentos, em vez de construir um novo motor e recarregar o modelo de linguagem cada vez.

// 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

Construir o pool uma vez amortiza o custo de carregar o modelo de linguagem em toda a execução em vez de pagar por isso por segmento.

3. Organizem o trabalho com um semáforo

Inicialize um SemaphoreSlim para o limite de concorrência e envolva-o em using. Cada tarefa chama WaitAsync() antes de começar e Release() em um finally, portanto, apenas o número permitido de segmentos está em andamento de uma vez.

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

A chamada WaitAsync() bloqueia até que um slot seja liberado, e o retorno do motor no finally interno entrega um motor pré-carregado diretamente para o próximo segmento aguardando.

4. Disponibilizar OcrInput por segmento

Envolva cada OcrInput em using para que seus bitmaps de página renderizados sejam liberados no momento em que o segmento é lido, antes que a próxima tarefa ocupe o slot.

PontasO using em OcrInput é o que impede a acumulação de memória de bitmap entre segmentos; sem isso, slots liberados ainda conteriam seus bitmaps de página.

Curtis Chau
Redator Técnico

Curtis Chau é bacharel em Ciência da Computação (Universidade Carleton) e se especializa em desenvolvimento front-end, com experiência em Node.js, TypeScript, JavaScript e React. Apaixonado por criar interfaces de usuário intuitivas e esteticamente agradáveis, Curtis gosta de trabalhar com frameworks modernos e criar manuais ...

Leia mais
Pronto para começar?
Nuget Baixar 6,136,090 | Versão: 2026.7 recém-lançado
Still Scrolling Icon

Ainda está rolando a tela?

Quer provas rápidas? PM > Install-Package IronOcr
executar um exemplo Veja sua imagem se transformar em texto pesquisável.