Newsgroup: it.comp.lang.visual-basic
Titolo: [!] [BT] test comparativi velocita' lettura file

Vi mando i risultati di un test che ho fatto per confrontare la velocita' di lettura di un file con alcuni linguaggi tra cui VB.
(per chi avesse gia' letto un mio post precedente simile a questo, ho rifatto i test perche' mi sono accorto di un errore che penalizzava il programma c++)
Per fare le prove, ho innanzitutto generato un file di testo di 46'000'000 byte (1'000'000 di linee di 46 byte), quindi ho scritto i programmi per leggerlo e ho confrontato i tempi.
Questi i risultati
codice C (chiamata diretta di API windows)
#include <windows.h>
#include <stdio.h>
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	HANDLE file;
	char s[100];
	DWORD read;
	BOOL ret;
	DWORD st, et;
	st = GetTickCount();
	file = CreateFile("a.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	do ret = ReadFile(file, s, 46, &read, NULL); while (!(ret &&  read == 0));
	CloseHandle(file);
	et = GetTickCount();
	sprintf(s, "%d\n", et - st);
	MessageBox(0, s, "", 0);
	return 0;
}
tempo 38 sec
codice C++ (utilizzo degli oggetti stream)
#include <windows.h>
#include <fstream.h>
#include <stdio.h>
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	DWORD st, et;
	char s[200];
	ifstream i("a.txt");
	st = GetTickCount();
	while (!i.eof()) i >> s;
	et = GetTickCount();
	i.close();
	sprintf(s, "%d\n", et - st);
	MessageBox(0, s, "", 0);
	return 0;
}
tempo 45 sec
codice VB
Sub Main()
   Dim fi As Integer
   Dim r As String
   Dim start As Date
   Dim sstop As Date
    fi = FreeFile
    Open "a.txt" For Input As fi
    start = Time
    Do While Not EOF(fi)
        Line Input #fi, r
    Loop
    sstop = Time
    Close fi
    start = sstop - start
    MsgBox start
End Sub
tempo (in compilato) 60 sec
codice pascal (x dos)
uses date;
var
   fi:text;
   r:string;
   start,stop:datiora;
begin
   assign(fi,'a.txt');
   reset(fi);
   getora(start);
   while not eof(fi) do readln(fi,r);
   getora(stop);
   close(fi);
   sottraiora(stop,start,start);
   writeln(strora(start,concentesimi));
end.
tempo 21 sec
a voi i commenti
--
Samuele Battarra
mail server: 
libero.it
account: battarsa

 ha scritto nel messaggio
news:zyhG6.7597$sb5.8...@news.infostrada.it...


 tempo (in compilato) 60 sec
Con il tuo codice (in compilato su PII 400 con Win2000) 44
secondi. Utilizzando il File System Object, piu' semplice da utilizzare
delle funzioni del VB il prog. e' anche molto piu' veloce:
Private Declare Function GetTickCount Lib "kernel32" () As Long
Sub Main()
    Dim r As String
    Dim start As Long
    Dim sstop As Long
    Dim fso As FileSystemObject
    Dim ts As TextStream
    Dim i As Long
    Set fso = New FileSystemObject
    Set ts = fso.OpenTextFile("a.txt")
    start = GetTickCount
    Do Until ts.AtEndOfStream
        r = ts.ReadLine
    Loop
    sstop = GetTickCount
    MsgBox (sstop - start)
End Sub
13 secondi (in compilato)
Ciao.

 tempo 21 sec
Ho rifatto i test sul mio PC utilizzando gli eseguibili che mi hai mandato
(senza virus, sembra ;-)):
Ho provato ogni programma 5 volte, con un file da 42Mb composto da 1Ml di
linee da 40byte (+ CrLf). Per ciascuno ho segnato il tempo maggiore (con il
file non in cache) e quello minore (con il file in cache). Avendo su questo
PC 256 Mb di Ram, la cache poteva contenerlo comodamente tutto.
             Minimo  Massimo
Pascal     3.85     11.09
C             5.735     7.877
C++         6.895     7.314
VB           7           8     (non mostra niente oltre i secondi)
A parte l'ovvio commento sulla differenza di velocità tra i nostri PC (<Big
Grin>), ho notato che durante l'esecuzione dei programmi, anche con il file
completamente in cache, la lucetta del disco si è saltuariamente accesa
brevemente per tutti i programmi tranne che per quello in Pascal sotto Dos.
Questo sembra dare conferma alla mia teoria che diceva che il programma DOS
gira in un suo spazio protetto e non rischia di swappare mai, mentre i
programmi Windows ogni tanto si trovano con dello loro funzionalità base
(leggi Dll) non immediatamente utilizzabili, e quindi perdono tempo.
L'alto tempo massimo impiegato dal programma Pascal sotto Dos, è spiegabile
con l'accesso a 16 bit invece che a 32 che i programmi Dos fanno al disco.
Per il resto i tempi dei 3 altri programmi sono simili, ed è abbastanza
normale visto che in fondo la maggior parte del tempo la perdono nelle
funzionalità di base per leggere sul disco. Per l'eseguibile VB mancano i
dati sotto al secondo (che comunque sono affidabili solo ad 1/18 di
secondo), quindi se consideri gli arrotondamenti direi che sono simili agli
altri.
Per testare veramente il Pascal servirebbe un programma similare ma in
Delphi.
Ciao
*Roby*

 13 secondi (in compilato)
Ho fatto la prova anche con 
VB.NET
:
Imports System
Imports System.IO
Module Prova
Sub Main()
Dim fs As FileStream = New FileStream("a.txt", FileMode.Open)
Dim sr As StreamReader = New StreamReader(fs)
Dim start, sstop As Integer
Dim s As String
start = Environment.TickCount
While (sr.Peek() > -1)
s = sr.ReadLine()
End While
sstop = Environment.TickCount
sr.Close()
fs.Close()
console.WriteLine((sstop - start).ToString & " millisecondi")
End Sub
End Module
Risultato: 4836 millisecondi. E ricordiamoci che si tratta di una beta
1.
Ciao.

programmi per leggerlo e ho confrontato i tempi. 
medie su 5 tentativi:
il tuo programma C: 34,9 secondi
il tuo programma C++: 41,4 secondi
(i sorgenti li ho modificati leggermente per trasformarli in programmi 
testuali)
programma in Pascal, compilato con Delphi 5:
program testpas;
{$APPTYPE CONSOLE}
uses
  SysUtils, Windows;
const
  sizeOfBuf = 46;
var
  buf: array[1..sizeOfBuf] of Byte;
 
  fhandle: Cardinal = 0;
  milli: Cardinal = 0;
  return: LongBool = True;
  read: Cardinal = 1;
begin
  milli:= GetTickCount;
  fhandle:= CreateFile(
    'testfile',
    GENERIC_READ,
    0,
    nil,
    OPEN_EXISTING,
    FILE_FLAG_SEQUENTIAL_SCAN,
    0);
  while return and (read > 0) do
    return:= Windows.ReadFile(fhandle, buf, sizeOfBuf, read, nil);
  CloseHandle(fhandle);
 
  WriteLn(GetTickCount - milli);
end.
totale: 33,4 secondi (e, mi permetto di aggiungere, nonostante 
l'overhead iniziale delle istruzioni - superflue - che "gentilmente" la 
Borland ha piazzato lě per noi poveri sviluppatori)
Non dimostra granchč :)
piů interessante č confrontare le dimensioni degli eseguibili generati:
testc.exe - 4096 byte (sta giusto giusto dentro l'allineamento...)
testcpp.exe - 25600 byte
testpas.exe - 41984 byte (ha tante di quelle porcherie dentro...)
ma non ci basta: che dire delle dll?
contando solo le librerie strettamente di runtime, le dimensioni 
"effettive" sono di:
testc.exe + msvcrt.dll =
  4096 + 295000 =
    299096 byte
testcpp.exe + libstdc++.dll + msvcrt.dll =
  25600 + 435925 + 295000 =
    756525 byte
testpas.exe =
     41984 byte
hehehehehe... sembra quasi che voglia a tutti i costi pubblicizzare 
Delphi :)
se foste interessati, il compilatore C/C++ che ho usato č il gcc 
versione 2.95.2-mingw, e queste sono le righe di comando che ho usato:
gcc testc.c -o testc.exe -O3 -Xlinker -s
g++ testcpp.cpp -o testcpp.exe -O3 -Xlinker -s

 
Fammi capire.
Prendiamo ad esempio il programma C.
Viene caricato in memoria e parte l'esecuzione.
Vengono chiamate le funzioni GetTickCount e CreateFileA.
Queste funzioni sono in kernel32.dll, che mi aspetto sia gia' in memoria. Anche se non lo fosse il tempo di caricamento delle funzioni non dovrebbe influire molto perche' viene fatto una sola volta.
Viene chiamata la funzione ReadFile.
Questa, dopo essere stata eventualmente caricata (anche lei e' in kernel32.dll) viene richiamanta ripetutamente, in quanto si trova all'interno di un ciclo.
Ora, mi spieghi perche' il codice di questa funzione, cosi' come tutto il codice contenuto nel breve ciclo, debba venir scaricato dalla memoria? (e' l'unico motivo per cui potrebbe trovarsi con alcune funzionalita' non immediatamente utilizzabili).
E, anche se fosse, non esiste, anche per un prg windows, il modo di farlo girare in un suo spazio protetto?


 
Ciao


 
4096 byte compilando il sorgente che ho postato io?
VC mi crea un exe da 28'672 byte, sia compilando l'originale, sia la versione testo (ho creato un'applicazione console sostituendo winmain con main, sprintf con printf ed eliminando MessageBox)
In compenso la versione testo usa solo kernel32.dll, che, essendo sicuramente gia' presente nel sistema, non va contata. Quindi i tuoi 41984 byte vengono battuti.


 
Fammi capire.
Prendiamo ad esempio il programma C.
Viene caricato in memoria e parte l'esecuzione.
Vengono chiamate le funzioni GetTickCount e CreateFileA.
Queste funzioni sono in kernel32.dll, che mi aspetto sia gia' in memoria. Anche se non lo fosse il tempo di caricamento delle funzioni non dovrebbe influire molto perche' viene fatto una sola volta.
Viene chiamata la funzione ReadFile.
Questa, dopo essere stata eventualmente caricata (anche lei e' in kernel32.dll) viene richiamanta ripetutamente, in quanto si trova all'interno di un ciclo.
Ora, mi spieghi perche' il codice di questa funzione, cosi' come tutto il codice contenuto nel breve ciclo, debba venir scaricato dalla memoria? (e' l'unico motivo per cui potrebbe trovarsi con alcune funzionalita' non immediatamente utilizzabili).
E, anche se fosse, non esiste, anche per un prg windows, il modo di farlo girare in un suo spazio protetto?


 
4096 byte compilando il sorgente che ho postato io?
VC mi crea un exe da 28'672 byte, sia compilando l'originale, sia la versione testo (ho creato un'applicazione console sostituendo winmain con main, sprintf con printf ed eliminando MessageBox)
In compenso la versione testo usa solo kernel32.dll, che, essendo sicuramente gia' presente nel sistema, non va contata. Quindi i tuoi 41984 byte vengono battuti.
--


 esempi e sarebbe stato ancora piu' veloce.
Certo, pero' il FSO e' fornito insieme al VB.
Ciao.

 il modo di farlo girare in un suo spazio protetto?
Ring 0? Non ho però idea di come farlo.
Ciao
*Roby*

 fisica, ma ti assicuro che è così. Altrimenti trovo inspiegabile il fatto
Tranquillo, non te lo chiedo, certi misteri debbono restare tali :(


 OE sa come dire al server di eliminare un msg appena postato [...].
Grazie per la spiegazione. 8-)
Ciao
*Roby*