Home

Benchmarks
Historie
Forum
Download
Helpware
Insider
Dokumentation
Kurs
Provider-Infos

Impressum



Die Performance

Benchmarks sind so eine Sache, meistens messen sie genau das, was man gerade messen will. Bei Internetdatenbanken schneidet die tdbengine aufgrund des ganz direkten Datenbankzugriffs in vielen Fällen sehr gut ab.

Im Anhang an dieses Artikels finden Sie ein kleines Benchmark-Programm, das auf diesem Server (Duron 700, Linux 2.2.16) folgende Ergebnisse liefert:
 
tdbengine: system performance

00:00:07.89  Fill_String_Fields
00:00:05.76  Fill_Number_Fields
00:00:01.35  Create_DataBase
00:00:00.33  Create_Index
00:00:00.77  FindIn_Index
00:00:01.02  FindIn_Index_And_Read
00:00:04.97  Create_Fulltext_Index
00:00:05.95  Find_Fulltext_Index

Die erste Zahl weist darauf hin, dass die tdbengine bei String-Operationen nicht unbedingt rasant ist. Hier wird ein Feld mit 40.000 Zufallsstrings gefüllt. Allerdings wird damit auch Perl seine Probleme haben, denn beide Systeme machen eine saubere dynamische String-Verwaltung.

Bei der zweiten Zahl werden ebenfalls 40.000 Zufallsstrings erzeugt, die allerdings nur aus Ziffern bestehen (Vor- und Nachkommateil einer Festkommazahl), anschließen werden die Zahlen in das interne Ansi-Double-Format konvertiert. Da die Zufallsstrings hier kürzer sind, ist auch die Ausführungszeit etwas geringer, denn die interne Zahlenverarbeitung ist recht flott.

Die dritte Zahl gibt die Zeit an, in der die zugehörige Datenbank erzeugt wird. Es werden also 10.000 Datensätze mit jeweils 4 alphanumerischen und 4 numerischen Datenfeldern erzeugt. Hier die genaue Struktur:
 
[STRUCTURE]
field_1=field_string_1,STRING,40
field_2=field_string_2,STRING,20
field_3=field_string_3,STRING,10
field_4=field_string_4,STRING,5
field_5=field_number_1,NUMBER,8,2
field_6=field_number_2,NUMBER,8,2
field_7=field_number_3,NUMBER,8,0
field_8=field_number_4,NUMBER,8,0

Die vierte Zahl zeigt die Zeit für die Erzeugung eines normalen (=BTree) hierarchischen Index über die ersten beiden Spalten der Tabelle.

Die fünfte Zahl zeigt die Zeit füe eine simulierte Suche über den gerade erzeugten Index. Es werden alle 10.000 Datensätze gesucht. Das Ergebnis ist m.E. recht imponierend.

Bei der sechsten Zahl kommt noch die Zeit für das Lesen der Datensätze hinzu. Es werden wiederum alle 10.000 Datensätze gesucht und gelesen.

Die siebte Zahl gibt die Zeit für die Erzeugung eines kompletten Volltextindex mit Feldinformation an.

Die letzte Zahl schließlich gibt die Zeit für eine simulierte feldbezogene Volltext-Suche an. Dabei werden im ersten numerischen Feld 100 Suchen ausgeführt mit folgenden Suchmustern: '00*', '01*', ... '99*'. Dabei werden schließlich alle Datensätze gefunden. Jede einzelne Suche liefert im Schnitt 100 Treffer. Hier zeigt sich, wo die besonderen Stärken der tdbengine liegen.

Und hier ist das Programm:

MODULE Benchmark

VAR String_Fields : STRING[,]
VAR Number_Fields : REAL[,]
VAR LegalChars : STRING
VAR max : INTEGER = 10000

PROCEDURE RandomString(length : INTEGER) : STRING
VAR result : STRING
VAR i : INTEGER
  nloop(i,length-1,result:=result+LegalChars[1+random(system.length(LegalChars))])
  RETURN result
ENDPROC

PROCEDURE RandomNumber(p1, p2 : INTEGER) : REAL
VAR s_result, t_result : STRING
VAR r_result : REAL
  s_result:=RandomString(p1)
  IF p2>0 THEN s_result:=s_result+'.'+RandomString(p2) END
  r_result:=val(s_result)
  RETURN r_result
ENDPROC

PROCEDURE Fill_String_Fields : REAL
VAR i : INTEGER
VAR now : REAL = system.now
  LegalChars:='abcdefghijklmnopqrstuvwxyzäöüß'
  nloop(i,max,String_Fields[i,0]:=RandomString(40))
  nloop(i,max,String_Fields[i,1]:=RandomString(20))
  nloop(i,max,String_Fields[i,2]:=RandomString(10))
  nloop(i,max,String_Fields[i,3]:=RandomString(05))
  RETURN system.now-now
ENDPROC

PROCEDURE Fill_Number_Fields : REAL
VAR i : INTEGER
VAR now : REAL = system.now
  LegalChars:='0123456789'
  nloop(i,max,Number_Fields[i,0]:=RandomNumber(10,2))
  nloop(i,max,Number_Fields[i,1]:=RandomNumber(05,2))
  nloop(i,max,Number_Fields[i,2]:=RandomNumber(10,0))
  nloop(i,max,Number_Fields[i,3]:=RandomNumber(05,0))
  RETURN system.now-now
ENDPROC

PROCEDURE Create_DataBase : REAL
VAR i, j, database, db_def, db_dat, x : INTEGER
VAR now : REAL = system.now
VAR db_name : STRING
  db_def:=rewrite('ramtext')
  writeln(db_def,'[STRUCTURE]')
  writeln(db_def,'field_1=field_string_1,STRING,40')
  writeln(db_def,'field_2=field_string_2,STRING,20')
  writeln(db_def,'field_3=field_string_3,STRING,10')
  writeln(db_def,'field_4=field_string_4,STRING,05')
  writeln(db_def,'field_5=field_number_1,NUMBER,8,2')
  writeln(db_def,'field_6=field_number_2,NUMBER,8,2')
  writeln(db_def,'field_7=field_number_3,NUMBER,8,0')
  writeln(db_def,'field_8=field_number_4,NUMBER,8,0')
  close(db_def)
  makedir('benchmark')
  db_name:='benchmark/benchmark.dat'
  delfile(db_name)
  MakeDB(db_name,'',0,'ramtext')
  db_dat:=opendb(db_name,'',0,15)
  i:=0
  WHILE i++<=max
    readrec(db_dat,0)
    nloop(j,3,setfield(db_dat,j+1,String_Fields[i,j]))
    nloop(j,3,setrfield(db_dat,j+5,Number_Fields[i,j]))
    x:=writerec(db_dat,filesize(db_dat)+1)
  END
  closedb(db_dat)
  RETURN system.now-now
ENDPROC

PROCEDURE Create_Index : REAL
VAR now : REAL = system.now
VAR db_dat : INTEGER
VAR db_name : STRING
  db_name:='benchmark/benchmark.dat'
  delfile('benchmark/bm.ind')
  db_dat:=opendb(db_name,'',0,15)
  genindex(db_dat,'$1,$2','bm.ind')
  closedb(db_dat)
  RETURN system.now-now
ENDPROC

PROCEDURE FindIn_Index : REAL
VAR now : REAL = system.now
VAR db_dat, i, x : INTEGER
VAR db_name : STRING = 'benchmark/benchmark.dat'
  db_dat:=opendb(db_name,'',0,0)
  WHILE i++<=filesize(db_dat)
    x:=findrec(db_dat,String_Fields[i,0]+','+String_Fields[i,1],'bm.ind',1)
    IF x=0 THEN cgiwriteln('index_error') END
  END
  closedb(db_dat)
  RETURN system.now-now
ENDPROC

PROCEDURE FindIn_Index_And_Read : REAL
VAR now : REAL = system.now
VAR db_dat, i, x : INTEGER
VAR db_name : STRING = 'benchmark/benchmark.dat'
  db_dat:=opendb(db_name,'',0,0)
  WHILE i++<=filesize(db_dat)
    x:=findrec(db_dat,String_Fields[i,0]+','+String_Fields[i,1],'bm.ind',1)
    IF x=0 THEN cgiwriteln('index_error') ELSE readrec(db_dat,x) END
  END
  closedb(db_dat)
  RETURN system.now - now
ENDPROC

PROCEDURE Create_Fulltext_Index : REAL
VAR now : REAL = system.now
VAR db_dat, index_dat, rel_dat, x : INTEGER
VAR db_name, index_name, rel_name : STRING
  db_name:='benchmark/benchmark.dat'
  index_name:='benchmark/index.dat'
  rel_name:='benchmark/index.rel'
  delfile(index_name)
  delfile(rel_name)
  genlist(index_name)
  genrel('benchmark','index',rel_name)
  db_dat:=opendb(db_name,'',0,0)
  index_dat:=opendb(index_name,'',0,15)
  rel_dat:=opendb(rel_name,'',0,15)
  x:=scanrecs(db_dat,index_dat,rel_dat,fields("complete"))
  closedb(rel_dat)
  closedb(index_dat)
  closedb(db_dat)
  RETURN system.now-now
ENDPROC

PROCEDURE Find_Fulltext_Index : REAL
VAR now : REAL = system.now
VAR db_dat, index_dat, rel_dat, x, i, j : INTEGER
VAR db_name, index_name, rel_name, searchstr : STRING
  db_name:='benchmark/benchmark.dat'
  index_name:='benchmark/index.dat'
  rel_name:='benchmark/index.rel'
  db_dat:=opendb(db_name,'',0,0)
  index_dat:=opendb(index_name,'',0,0)
  rel_dat:=opendb(rel_name,'',0,0)
  WHILE i<10 DO
    j:=0
    WHILE j<10 DO
      searchstr:='field_number_1:'+str(i)+str(j)+'*'
      x:=x+MarkTable(db_dat,index_dat,searchstr,'0123456789','',0,rel_dat)
      j++
    END
    i++
  END
  IF x<>max THEN cgiwriteln('fulltext_index_error') END
  closedb(rel_dat)
  closedb(index_dat)
  closedb(db_dat)
  RETURN system.now-now
ENDPROC

PROCEDURE Main
  cgiwriteln('content-type: text/plain')
  cgiwriteln('')
  cgiwriteln('tdbengine: system performance')
  cgiwriteln('')
  InitArray(String_Fields[max,3]); InitArray(Number_Fields[max,3]);
  cgiwriteln(timestr(Fill_String_Fields,2)+'  '+'Fill_String_Fields')
  cgiwriteln(timestr(Fill_Number_Fields,2)+'  '+'Fill_Number_Fields')
  cgiwriteln(timestr(Create_DataBase,2)+'  '+'Create_DataBase')
  cgiwriteln(timestr(Create_Index,2)+'  '+'Create_Index')
  cgiwriteln(timestr(FindIn_Index,2)+'  '+'FindIn_Index')
  cgiwriteln(timestr(FindIn_Index_And_Read,2)+'  '+'FindIn_Index_And_Read')
  cgiwriteln(timestr(Create_Fulltext_Index,2)+'  '+'Create_Fulltext_Index')
  cgiwriteln(timestr(Find_Fulltext_Index,2)+'  '+'Find_Fulltext_Index')
ENDPROC
Hier nochmal tabellarisch die Auswertung
 
Fill_String_Fields 00:00:07.89 Anlage eines String-Arrays mit 40.000 Zufallsstrings, jeweils 10.000 der Länge 40, 20, 10 und 5
Fill_Number_Fields 00:00:05.76 Anlage eines Real-Arrays mit 40.000 Zufallszahlen, jeweils 10.000 mit 12 Ziffern mit zwei Nachkommastellen, 10 Ziffern ohne Nachkommastelle, 7 Zifffern mit zwei Nachkommastellen und 5 Ziffern ohne Nachkommastelle. Sie Zahlen werden aus Zufallsstrings gewonnen. 
Create_DataBase 00:00:01.35 Anlage einer Datenbank aus den zuvor generierten String- und Zahlenfeldern.Die Datenbank besteht aus einer Tabelle mit 10.000 Zeilen zu je 8 Spalten.
Create_Index 00:00:00.33 Anlage eines hierarchischen Index über die soeben generierte Datenbank. Indiziert weden die ersten beiden Felder.
FindIn_Index 00:00:00.77 Es werden alle 10.000 Datensätze im Index gesucht (und gefunden)
FindIn_Index_And_Read 00:00:01.02 Es werden alle 10.000 Datensätze im Index gesucht und dann in der Datenbank auch gelesen.
Create_Fulltext_Index 00:00:04.97 Anlage eines (feldbezogenen) Volltext-Index über alle Felder (Spalten) der Datenbank.
Find_Fulltext_Index 00:00:05.95 100 Volltextsuchen in der ersten Spalten der Tabelle. Jede Suche liefert im Mittel 100 Treffer. Es werden alle Zeilen gefunden.