I'm not here to put down any storage option available. I just wanted
to share with you guys another little experiment I did with GOODS,
OmniBase, Magma, MySQL, and MySQL with RubyOnRails.
The test consisted of an extremely simplistic process of updating a
sequence number in a dictionary. In a typical CRM-like application, I
would use something like this in order to "generate" a customer ID
number for new customers (unless someone has a better suggestion). In
essence, the test updates a single dictionary element (a counter)
1000 times. I'm sure the code could be optimized so I'm open to
suggestions on how to do so.
My intentions here were not really to compare an OODB against a RDB.
You may decide to ignore the MySQL results.
The GOODS code looked like this:
time:= Time millisecondsToRun:
[Transcript cr..
db := KKDatabase onHost: 'localhost' port: 6100.
db root at: 'Sequences' put: (Dictionary new).
db commit.
base := db root at: 'Sequences'.
nextCustomerNo := base at: 'CustomerNo' ifAbsent: [0].
[0 to: 999 do:
[:i|
(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
nextCustomerNo := nextCustomerNo + 1.
base at: 'CustomerNo' put: nextCustomerNo.
db commit]] ensure: [db logout]].
Transcript cr; show: (time/1000) asFloat; show: ' seconds'.
db := KKDatabase onHost: 'localhost' port: 6100.
base := db root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:
'CustomerNo').
db logout.
----------------------------------------------------------------------
The OmniBase code looked like this:
time:= Time millisecondsToRun:
[Transcript cr..
db := OmniBase openOn: 'Macintosh HD:Users:dsalama:OB'.
[OmniBase root at: 'Sequences' put: (Dictionary new)]
evaluateAndCommitIn: db newTransaction.
[base := OmniBase root at: 'Sequences'.
base at: 'CustomerNo' put: 0.
base markDirty.
] evaluateAndCommitIn: db newTransaction.
[0 to: 999 do:
[:i|
(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
[base := OmniBase root at: 'Sequences'.
nextCustomerNo := (base at: 'CustomerNo') + 1.
base at: 'CustomerNo' put: nextCustomerNo.
base markDirty] evaluateAndCommitIn: db newTransaction.
]] ensure: [db close]].
Transcript cr; show: (time/1000) asFloat; show: ' seconds'.
db := OmniBase openOn: 'Macintosh HD:Users:dsalama:OB'.
base := (db newTransaction) root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:
'CustomerNo').
db close.
----------------------------------------------------------------------
The Magma code looked like this:
time:= Time millisecondsToRun:
[Transcript cr..
db := MagmaSession hostAddress: #(127 0 0 1) asByteArray port: 51969.
db connectAs: 'dsalama'.
db commit:
[db root at: 'Sequences' put: (Dictionary new)].
base := db root at: 'Sequences'.
db commit:
[base at: 'CustomerNo' put: 0].
[0 to: 999 do:
[:i|
(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
base := db root at: 'Sequences'.
nextCustomerNo := (base at: 'CustomerNo') + 1.
db commit: [base at: 'CustomerNo' put: nextCustomerNo].
]] ensure: [db disconnect]].
Transcript cr; show: (time/1000) asFloat; show: ' seconds'.
db := MagmaSession hostAddress: #(127 0 0 1) asByteArray port: 51969.
db connectAs: 'dsalama'.
base := db root at: 'Sequences'.
Transcript cr; show: 'Last Customer No: '; show: (base at:
'CustomerNo').
db disconnect.
----------------------------------------------------------------------
The MySQL code looked like this:
time:= Time millisecondsToRun:
[Transcript cr..
Socket initializeNetwork.
spec := (JdmConnectionSpec new initialize
user: 'master'; password: 'secret';
host: (NetNameResolver addressForName: 'localhost');
database: 'dcm'; port: 3306).
connection := JdmConnection on: spec.
statement := connection createStatement.
resultSet := connection createStatement executeQuery: 'insert into
tests values (null, ''CustomerNo'', 0)'.
0 to: 999 do:
[:i|
(i \\ 100) = 0 ifTrue: [Transcript show: '.'].
connection := JdmConnection on: spec.
resultSet := connection createStatement executeQuery: 'select
next from tests where sequence = ''CustomerNo'''.
resultSet next.
nextCustomerNo := (resultSet valueNamed: 'next') + 1.
connection close.
[connection := JdmConnection on: spec.
updateSQL := 'update tests set next = ', (nextCustomerNo
asString), ' where id = 1'.
connection createStatement executeQuery: updateSQL]
ensure: [connection close]]].
Transcript cr; show: (time/1000) asFloat; show: ' seconds'.
connection := JdmConnection on: spec.
statement := connection createStatement.
resultSet := statement executeQuery: 'select next from tests where
sequence = ''CustomerNo'''.
resultSet next.
nextCustomerNo := (resultSet valueNamed: 'next').
Transcript cr; show: 'Last Customer No: '; show: nextCustomerNo.
connection close.
Granted, that after Alan and Boris explained to me the "limitation"
of the MySQL driver, there is additional overhead in opening and
closing the connection 1000 times.
----------------------------------------------------------------------
And the RubyOnRails code looked like this:
require 'test'
t0 = Time.now
0.upto(999) do |i|
puts "." if (i % 100) == 0
t = Test.find_by_sequence('CustomerNo')
unless t
t = Test.new
t.sequence = 'CustomerNo'
t.next = 0
end
t.next += 1
t.save
end
t1 = Time.now
puts (t1-t0).to_s + ' seconds'
puts "Last Customer No: " +
Test.find_by_sequence('CustomerNo').next.to_s
----------------------------------------------------------------------
The tests were performed on a Powerbook G4 1.5GHz with 1GB RAM with
Squeak 3.7-5989-full with the latest(?) versions of the respective
class libraries.
GOODS: 201.553 seconds
Omnibase: 3.102 seconds
Magma: 73.578 seconds
MySQL: 13.815 seconds
RubyOnRails: 12.411535 seconds
Interestingly the MySQL results were not much better (or worse),
whether you use Squeak or RubyOnRails. It's simply a point of
comparison for the other tests.
I was definitely impressed with OmniBase's performance. As I had said
in previous postings, OB has provided me the best performance so far
during my tests and every day I feel more confident in OB. Also, as
I mentioned in the past, I am now getting ready to purchase a
commercial license for it and hope to get the Linux file locking
support from Avi as well. Also, I hope my move towards purchasing a
license motivates others (e.g. Cees de Groot) to help David support
and maintain OB. I also wish there was better documentation for it.
I was a bit disappointed at the performance of GOODS. I like the
GOODS server and the people I have talked with regarding its
performance under "heavier" loads are very happy with it. Again, as
I mentioned in previous postings, they are not using Smalltalk. They
are using Java or C. As Avi said, it could be a performance tuning
issue with the Squeak GOODS classes and hopefully that would improve
over time.
Now, I hope my findings are useful to others. However, I would love
to hear feedback from others regarding this. I tried to make these
as equal and unbiased as possible. Also, the fact that I'm new to
all this may affect the quality and optimization of my code.
Thanks,
Daniel
Daniel Salama