Hi all,

To see what the differences in speed are between MSEdb and fcl-db I did
some benchmarks. (See the attachment for the code and results)

I think that the first conclusion is that when it comes to executing
queries, opening connections to the database and such, the 'real sql-
work' there's no real difference. Which was to be expected, since the
code in that areas is practically the same.

But as soon the TBufDataset is used in the area it's designed for, there
are differences. Also not that strange, since this is the part that is
rewritten in MSEdb.

If you use a large dataset, (100000 records, about 2020 bytes for each
record) simply opening the dataset and fetching all records is 7 times
slower with MSEdb. (OpenFetch test)

Further, when you 'scroll' through all records, and read-out all data,
MSEdb needs more time for every record. (ReadData test, see graph) 

The result is that if the dataset is opened, and scrolled from the begin
to the end and back again once, fcl-db is 6 times faster. If you do this
10 times, fcl-db is 'only' three times as fast. But the absolute time
difference is larger. 

If you open this same dataset, end change the first 20.000 records.
MSEdb is faster then fcl-db. (about 1.2 times) Thus you can conclude
that with editing large amount of datasets, msedb is faster, it so fast
that it overcomes the browse-slowlyness.

I think that the reason for the difference in editing is simple: to
spare memory fcl-db doesn't store the location of the edit-buffer with
each record-buffer. So it has to search the edit-buffer for every record
you want to change. I did that on purpose, since I thought that editing
this amount of records, without applying the changes, isn't something
that you do in a regular program.

If it comes to the use of RecNo, msedb beats fcl-db. Fetching the data
msedb is slower, offcourse. but then for each 'jump' it consumes almost
no time at all. (see JumprecNo graph, the first 'dot' is more a
randomness-thing if you use only 5 points)

But, on the other hand, if you use bookmarks to do the same thing. fcl-
db behaves just like msedb. Jumping through the records is fast.


I tried to explain the readdata-difference. At first I thought that
msedb converted all strings from string to widestring to string. But it
doesn't do that. (see my mails on this subject)

I think that the only reasonable explanation is the argument from
Michael: using ansistrings/pointers to store the data instead of storing
them directly into a buffer, induces so much overhead that it becomes
too slow.

Overall, I would conclude that the current design of the TBufDataset of
fcl-db is a better choice then the design of tmseBufDataset. Since fcl-
db is faster on those things a TBufDataset is build/meant for: browsing
through records.

Discussion: What tests could I do more? Is there something I overlooked?

Regards,
  Joost.
program bench;

{$mode objfpc}{$H+}

{ $Define SQLDB}

uses
  cthreads, Classes, SysUtils, db,
{$IFDEF SQLDB}
   sqldb, ibconnection;
{$ELSE}
   msqldb,mseibconnection;
{$ENDIF}
var AConn   : TSQLConnection;
    ATrans  : TSQLTransaction;
    AQuery  : TSQLQuery;

procedure CreateTable;

begin
  try
    AConn.ExecuteDirect('drop table benchtest');
  except
    // do nothing
  end;
  AConn.ExecuteDirect('create table benchtest (id int, shortstr varchar(10), longstr varchar(2000))');
  ATrans.CommitRetaining;
end;

procedure FillData;

  function GetRandomStr(MaxSize : Integer) : String;
  var b,size : integer;
      a      : string;
  begin
    Size := Random(MaxSize);
    SetLength(a,size);
    for b := 1 to Size do
      a[b] := chr(Random(28)+65);
    Result := a;
  end;

var i : integer;

begin
  Randomize;
  AQuery.SQL.Text := 'insert into benchtest (id,shortstr,longstr) values (:id,:shortstr, :longstr)';
  for i := 0 to 100000 do
    begin
    AQuery.params[0].asinteger := i;
    AQuery.Params[1].AsString := GetRandomStr(10);
    AQuery.Params[2].AsString := GetRandomStr(2000);
    AQuery.ExecSQL;
    end;
  ATrans.CommitRetaining;
end;

Procedure ReadData(times : integer);
var s : string;
    i : integer;
begin
  AQuery.SQL.Text := 'select * from benchtest';
  AQuery.Open;
  for i := 0 to times -1 do
    begin
    while not AQuery.EOF do
      begin
      s := AQuery.Fields[1].AsString;
  //    writeln(s);
      s := AQuery.Fields[2].AsString;
  //    writeln(s);
      AQuery.Next;
      end;

    while not AQuery.BOF do
      begin
      s := AQuery.Fields[1].AsString;
  //    writeln(s);
      s := AQuery.Fields[2].AsString;
  //    writeln(s);
      AQuery.Prior;
      end;
    end;

  AQuery.Close;
end;


Procedure OpenFetch;
var s : string;
begin
  AQuery.SQL.Text := 'select * from benchtest';
  AQuery.Open;
  AQuery.Last;
  AQuery.Close;
end;


Procedure JumprecNo(jumps : integer);
var s : string;
    i : integer;
    r : integer;
begin
  AQuery.SQL.Text := 'select * from benchtest';
  AQuery.Open;
  for i := 0 to jumps do
    begin
    r := Random(99999);
    AQuery.RecNo := r;
    if AQuery.Fields[0].asinteger <> (r-1) then writeln('He');
    end;
  AQuery.Close;
end;


Procedure JumpBookmark(jumps : integer);
var s : string;
    i : integer;
    r : integer;
    bms : array[0..9999] of TBookmark;
    
begin
  Randomize;
  AQuery.SQL.Text := 'select * from benchtest';
  AQuery.Open;
  
  i := 0;
  while not AQuery.EOF do
    begin
    inc(i);
    if (i mod 10)=0 then bms[i div 10] := AQuery.GetBookmark;
    s := AQuery.Fields[1].AsString;
//    writeln(s);
    s := AQuery.Fields[2].AsString;
//    writeln(s);
    AQuery.Next;
    end;


  for i := 0 to jumps do
    begin
    r := Random(9998)+1;
    AQuery.GotoBookmark(bms[r]);
    if AQuery.Fields[0].asinteger+1 <> (r*10) then writeln('He');
    end;
  AQuery.Close;
end;


Procedure OpenClose(count : integer);
var i : integer;
begin
  AQuery.SQL.Text := 'select * from benchtest where id < 15';
  for i := 0 to count -1 do
    begin
    AQuery.Open;
    AQuery.Close;
    end;
end;


Procedure EditData(Records : longint);
var s : string;
    i : integer;
begin
  AQuery.ParseSQL := True;
  AQuery.ReadOnly := False;
  AQuery.SQL.Text := 'select * from benchtest';
  AQuery.Open;
  for i := 0 to Records -1 do
    begin
    AQuery.Edit;
    AQuery.Fields[1].AsString := 'TEST';
    AQuery.Fields[2].AsString := 'TESTING123TESTING123TESTING123TESTING123TESTING123TESTING123TESTING123';
    AQuery.Next;
    if AQuery.EOF then break;
    end;

  while not AQuery.BOF do
    begin
    s := AQuery.Fields[1].AsString;
    if s <> 'TEST' then writeln('PROBLEM');
    s := AQuery.Fields[2].AsString;
    if s <> 'TESTING123TESTING123TESTING123TESTING123TESTING123TESTING123TESTING123' then writeln('PROBLEM2');
    AQuery.Prior;
    end;

  AQuery.Close;
end;


begin
{$IFDEF SQLDB}
  AConn := tibconnection.Create(nil);
{$ELSE SQLDB}
  AConn := tmseibconnection.Create(nil);
{$ENDIF SQLDB}
  AConn.UserName := 'sysdba';
  AConn.DatabaseName := '/opt/firebird/data/testdb.fdb';
  AConn.Password := 'password';
//  AConn.DatabaseName := '192.168.3.1:/home/firebird/fpctest.fdb';
//  AConn.Password := 'password';

  ATrans := TSQLTransaction.Create(nil);
  ATrans.DataBase := AConn;
  AConn.Transaction := ATrans;
  
  AQuery := TSQLQuery.Create(nil);
  AQuery.Transaction := ATrans;
  AQuery.DataBase := AConn;
  AQuery.ParseSQL := False;

//  CreateTable;
//  FillData;
//  JumprecNo(500);
//  ReadData(10);
//  OpenFetch;
  JumpBookmark(50);
//  EditData(20000);
//  OpenClose(50);
end.

Attachment: db_benchmarks.ods
Description: application/vnd.oasis.opendocument.spreadsheet

_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply via email to