Cleverton Bueno
← Voltar para todos os artigos

Otimização Extrema em C++: Acelerando 600.000 Registros de 1 Semana para 15 Minutos

Revela as estratégias de otimização, como a conversão para CSV e o uso de DBF para indexação, que foram a chave para reduzir um processamento de dados de uma semana para apenas 15 minutos.

No artigo anterior, desvendamos a arquitetura flexível que permitiu nossa nova aplicação entender praticamente qualquer layout de planilha (leia também o artigo "Construindo um Motor de ETL Flexível: Mapeamento Dinâmico de Planilhas com C++"). Hoje, vamos atacar a questão mais dramática: como um processo que levava uma semana passou a ser executado em menos tempo do que leva para tomar um café? A resposta não está em nenhuma "magia negra" da programação, mas sim em decisões de arquitetura e em um profundo entendimento sobre onde estão os verdadeiros gargalos no processamento de dados.

Anatomia da Lentidão: Por que o Processo Antigo Falhava?

Quando afirmei que o sistema antigo era lento em tudo, não era um exagero. Ele sofria de dois problemas técnicos fundamentais que, combinados, criavam o gargalo perfeito:

  • O Vilão da Automação OLE (Object Linking and Embedding): A aplicação legada lia as planilhas do Excel diretamente, célula por célula. Tecnicamente, isso significa que para cada uma dos milhões de células, o programa precisava "conversar" com o Excel, pedir o valor, esperar a resposta e só então continuar. Essa comunicação entre processos é extremamente custosa e lenta. Multiplique essa sobrecarga por 600.000 linhas e dezenas de colunas, e você terá dias de processamento.
  • O Custo do Processamento em Memória: Para cruzar informações (por exemplo, encontrar o endereço de um CPF que estava no arquivo de renovação), a abordagem tradicional seria carregar os dados em grandes listas ou arrays na memória. Para um volume de 600.000 registros, isso se torna inviável. A tentativa de ordenar ou buscar dados em listas tão grandes consome uma quantidade imensa de RAM e tempo de processador, muitas vezes forçando o sistema operacional a usar o disco como memória virtual (um processo conhecido como swapping), o que degrada a performance drasticamente.

A Estratégia Vencedora: Um Pipeline de Dados Otimizado em 3 Etapas

A nova solução em C++ abandonou completamente a abordagem linear. Em vez disso, eu crei um pipeline de processamento em lotes, onde cada etapa é otimizada para uma tarefa específica.

Etapa 1: Converter, Não Ler (Excel -> CSV)

A primeira decisão foi contraintuitiva: para ler um arquivo Excel, não o lemos diretamente. Usamos o próprio Excel para fazer o trabalho pesado. A função DevolveCsv em automato.cpp é a responsável por isso. Ela executa um único comando de Automação OLE: "Salvar Como CSV".
Por que isso é rápido? A operação de salvar um arquivo é uma das funções mais otimizadas de qualquer software. Ao fazer isso, delegamos a tarefa complexa de analisar o formato binário .xlsx para o código nativo e veloz do próprio Excel. Em segundos, temos um arquivo de texto puro (CSV), que é um formato universalmente simples e incrivelmente rápido para qualquer linguagem de programação ler.

Etapa 2: Indexar, Não Ordenar (CSV -> DBF)

Com o CSV em mãos, poderíamos lê-lo para a memória. Mas, como vimos, isso seria um suicídio de performance. Aqui entra o "pulo do gato" da solução: usamos o formato de banco de dados DBF como um repositório temporário de alta velocidade.
Dentro de uma função, a aplicação lê o arquivo CSV linha por linha e, usando componentes ADO, insere os dados em tabelas DBF criadas dinamicamente para aquela execução.
Por que o DBF? Embora seja uma tecnologia mais antiga, o formato DBF, com seus arquivos de índice associados, é fenomenal para uma tarefa: ordenação e busca rápida de dados em disco. Pense no conjunto DBF/NTX não como uma simples tabela, mas como um motor de busca especializado e leve. Ao criarmos o índice, estávamos, na prática, construindo um 'Google' para os nossos dados locais, permitindo que a consulta Locate encontrasse qualquer registro instantaneamente. Ao carregar os 600.000 registros em uma tabela DBF e criar um índice na coluna CPF, por exemplo, estamos pré-computando a localização de cada registro. Isso elimina a necessidade de qualquer algoritmo de ordenação em memória.

Etapa 3: Processar com Inteligência (Consultas Indexadas)

Esta é a etapa onde colhemos os frutos. O loop principal de processamento, também em B_ProsClick, não itera sobre um arquivo de texto, mas sim sobre a tabela DBF principal (por exemplo, a de renovação cadastral).
Quando o programa precisa dos dados de telefone e endereço para um CPF específico, ele não faz uma busca linear em outro arquivo de 600.000 linhas. Ele executa um comando simples e poderoso:

Snippet de Código 1:

Delphi/C++ Builder
DadosDM->ADOTable2->Locate("CPF", s, Optpart1);

Por baixo dos panos, o Locate utiliza o índice do arquivo DBF para saltar diretamente para o registro desejado, uma operação que leva uma fração de milissegundo, não importa se a tabela tem mil ou um milhão de linhas. <;p>

Comparativo de Abordagens

A diferença na estratégia fica clara quando comparamos os dois fluxos:

  • Abordagem Antiga (Linear e Lenta):
    • Para cada linha na planilha:
      • Inicia comunicação OLE.
      • Lê célula A1, A2, A3... (alto custo por célula).
      • Busca dados correspondentes em outra lista gigante na memória (lento).
      • Escreve uma linha no script de saída.
  • Nova Abordagem (Lotes e Indexada):
    • Lote 1 (Conversão Rápida): Um único comando OLE converte o Excel inteiro para CSV.
    • Lote 2 (Carga e Indexação): Uma leitura sequencial rápida do CSV para o DBF, que automaticamente cria os índices.
    • Lote 3 (Processamento Otimizado): Para cada linha na tabela principal, faz buscas quase instantâneas nas tabelas secundárias usando os índices.

Conclusão: Performance é Arquitetura

O salto de uma semana para 15 minutos não foi alcançado com algoritmos complexos ou otimizações de baixo nível. Ele foi o resultado de uma arquitetura de processamento de dados que respeita uma regra fundamental da computação: minimize as operações lentas. Ao trocar milhões de chamadas OLE por uma única e substituir buscas lentas em memória por consultas indexadas em disco, atacamos a raiz do problema de performance. A lição é clara: muitas vezes, a maior otimização não está no código que você escreve, mas no design do fluxo de dados que o seu código processa.
Com um sistema flexível para entender os dados e um motor ultra-rápido para processá-los, as principais dores do dia a dia estavam resolvidas. Mas como poderíamos levar essa ferramenta a um novo patamar, permitindo que ela se adaptasse a demandas completamente novas, sem nenhuma alteração no código? No nosso último artigo, exploraremos a funcionalidade mais poderosa da aplicação: o Script Livre (leia também o artigo ""Indo Além do Fixo: Criando um Gerador de Scripts SQL Dinâmico e Configurável pelo Usuário").