Può capitare di dover analizzare con un programma Uniface file di testo di grandi dimensioni, ad esempio file di log. In genere, specialmente se i files non sono molto grandi li si carica in memoria con una fileload e si analizzano le righe che costituiscono il file con un loop quasi sempre così costituito:
1 – cerco il terminatore di riga
2 – se non lo trovo termino perché sono arrivato al fondo del file
3 – isolo il contenuto della linea identificata
4 – lavoro la riga e accumulo su una DUMMY il contenuto che interessa
5 – ricomincio
6 – scarico il contenuto della DUMMY su un nuovo file di testo
Vi siete mai chiesti:
A) le performance che si ottengono con questo loop sono valide per files di testo di qualunque dimensione?
B) lo scarico finale su nuovo file di testo può essere migliorato?
Bene, vi racconto la mia ultima esperienza pratica, che da una significativa risposta a queste due domande!
Dovevo analizzare file di LOG di Uniface generati su una installazione multiaziendale con circa 200 utenti; la dimensione minima di questi files era di circa 50MB pari a circa 1.000.000 di righe ma alcuni erano costituiti da milioni di righe, il più grande poco più di 8 milioni. Avevo già scritto il programma di analisi ed era stato testato su file di dimensioni decisamente minori; provando a riutilizzarlo su file di queste dimensioni diventava praticamente inutilizzabile!
A) Il criterio di stacco della riga usando la ricerca del terminatore è valido solo per file di piccole dimensioni quando il file diventa più corposo, ossia maggiore di qualche decina di migliaia di righe conviene decisamente utilizzare il driver TXT. La ricerca del terminatore di riga e lo stacco della riga durava decimi di secondo mentre con il driver TXT dura millesimi di secondo. Ho predisposto nell’Application Model che utilizzo da molto tempo per questo tipo di funzioni una entità:
TXTFILE:
myPK C(1)
myRec C*
in modo da poter eventualmente gestire file di testo organizzati a tipi di record in funzione del valore del primo carattere.
La sequenza di istruzioni:
vEOL = $scan(vBuffer[vIni], cCR)
if (vEOL = 0) break
vLinea = vBuffer[vIni, vIni+vEOL-1]
… lavoro la riga …
vIni = vIni + vEOL
è diventata:
setocc “TXTFILE”, $curocc(TXTfile)+1
if ($status < 0) break
vLinea = $concat(myPK, myRec)
… lavoro la riga …
L’occupazione di memoria del processo nei due casi è simile, ma una considerazione collaterale va fatta: il caricamento del file di testo in memoria con la fileload dura circa 10 secondi, mentre la retrieve iniziale con il driver TXT dura quasi il doppio (18 sec) ma è ampiamente ripagata dalla miglioria nello scorrimento sulle occorrenze.
I tempi sono migliorati di circa 800 volte!
B) La funzione suddetta accumula in una entità DUMMY costituita da un singolo campo DUMMYLINE C* i contenuti necessari. Ad esempio, al termine dell’elaborazione di 2.000.000 di record mi sono ritrovato circa 250.000 record nell’entità DUMMY. In origine la mia applicazione era costituita da questo loop:
filedump cNul, vFilename
forentity “DUMMY”
filedump/append $concat(dummyline.dummy, cCR), vFilename
endfor
Anche in questo caso i tempi si allungavano in modo importante al crescere del numero di occorrenze gestite. Ho sostituito la creazione progressiva del file con la creazione in memoria di una lista, sostituendo successivamente con una istruzione utilizzante la $replace() i separatori di lista con i terminatori di riga; il nuovo loop è:
filedump cNul, vFilename
vContent = cNul
putlistitems vContent, dummyline.dummy
vContent = $replace(vContent, 1, cSep, cCR, -1)
vContent = $concat(vContent, cCR)
filedump/append vContent, vFilename
Anche in questo caso i tempi sono migliorati notevolmente di circa 300 volte!
Con le due azioni elencate sono riuscito su una macchina virtuale W10 dotata di 4GB di RAM a leggere ed analizzare un LOG di 2.023.616 righe, ordinare le circa 250.000 righe costituenti il risultato ottenuto e scaricarlo su un nuovo file di testo in poco più di 4 minuti, per la precisione 4 minuti e 24″.
Non male…non male!!!
Chi volesse maggiori informazioni può contattarmi a gianni.sandigliano@unifacesolutions.com