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:
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.
- Para cada linha na planilha:
- 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").