public
Rubygem
Description: DataMapper - Core
Homepage: http://datamapper.org
Clone URL: git://github.com/sam/dm-core.git
commit  fc0435161512b11ed5faf6e12364a9f4976bed3e
tree    41747cfc9807be2f509599b540c5746ea0e8d572
parent  2f42d99c96158618143ce7565c11d97c249ba166
dm-core / script / performance.rb
100755 287 lines (237 sloc) 12.373 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#!/usr/bin/env ruby
 
require File.join(File.dirname(__FILE__), '..', 'lib', 'data_mapper')
 
require 'benchmark'
require 'rubygems'
 
require 'faker'
require 'active_record'
 
socket_file = Pathname.glob(%w[
/opt/local/var/run/mysql5/mysqld.sock
tmp/mysqld.sock
tmp/mysql.sock
/var/mysql/mysql.sock
]).find(&:socket?)
 
configuration_options = {
  :adapter => 'mysql',
  :username => 'root',
  :password => '',
  :database => 'data_mapper_1'
}
 
configuration_options[:socket] = socket_file unless socket_file.nil?
 
ActiveRecord::Base.logger = Logger.new(DataMapper.root + 'log/ar.log')
ActiveRecord::Base.logger.level = 0
 
ActiveRecord::Base.establish_connection(configuration_options)
 
class ARExhibit < ActiveRecord::Base #:nodoc:
  set_table_name 'exhibits'
end
 
ARExhibit.find_by_sql('SELECT 1')
 
DataMapper::Logger.new(DataMapper.root + 'log/dm.log', :debug)
adapter = DataMapper.setup(:default, "mysql://root@localhost/data_mapper_1?socket=#{socket_file}")
 
class Exhibit
  include DataMapper::Resource
 
  property :id, Integer, :serial => true
  property :name, String
  property :zoo_id, Integer
  property :notes, String, :lazy => true
  property :created_on, Date
  property :updated_at, DateTime
 
end
 
touch_attributes = lambda do |exhibits|
  [*exhibits].each do |exhibit|
    exhibit.id
    exhibit.name
    exhibit.created_on
    exhibit.updated_at
  end
end
 
exhibits = []
 
10_000.times do
  exhibits << [
# 'INSERT INTO `exhibits` (`name`, `zoo_id`, `notes`, `created_on`, `updated_at`) VALUES (?, ?, ?, ?, ?)'
    'INSERT INTO `exhibits` (`name`, `zoo_id`, `notes`, `created_on`) VALUES (?, ?, ?, ?)',
    Faker::Company.name,
    rand(10).ceil,
    Faker::Lorem.paragraphs.join($/),
    Date.today,
  ]
end
 
Benchmark.bmbm(60) do |x|
  # reset the exhibits
  x.report do
    adapter.execute "DROP TABLE `exhibits`"
    adapter.execute <<-EOS.compress_lines
CREATE TABLE `exhibits` (
`id` INTEGER(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50),
`zoo_id` INTEGER(11),
`notes` TEXT,
`created_on` DATE,
`updated_at` TIMESTAMP NOT NULL,
PRIMARY KEY(`id`)
)
EOS
  end
 
  x.report('DO.execute insert x10,000') do
    10_000.times { |i| adapter.execute(*exhibits.at(i)) }
  end
 
  x.report('AR.id x10,000') do
    ActiveRecord::Base::uncached do
      10_000.times { touch_attributes[ARExhibit.find(1)] }
    end
  end
 
  x.report('DM.id x10,000') do
    10_000.times { touch_attributes[Exhibit.get(1)] }
  end
 
  x.report('AR.id x10,000 (cached)') do
    ActiveRecord::Base::cache do
      10_000.times { touch_attributes[ARExhibit.find(1)] }
    end
  end
 
  x.report('DM.id x10,000 (cached)') do
    repository(:default) do
      10_000.times { touch_attributes[Exhibit.get(1)] }
    end
  end
 
  x.report('AR.first x10,000') do
    10_000.times { touch_attributes[ARExhibit.find(:first)] }
  end
 
  x.report('DM.first x10,000') do
    10_000.times { touch_attributes[Exhibit.first] }
  end
 
  x.report('AR.all limit(100) x1,000') do
    ActiveRecord::Base::uncached do
      1000.times { touch_attributes[ARExhibit.find(:all, :limit => 100)] }
    end
  end
 
  x.report('DM.all limit(100) x1,000') do
    1000.times { touch_attributes[Exhibit.all(:limit => 100)] }
  end
 
  x.report('AR.all limit(100) x1,000 (cached)') do
    ActiveRecord::Base::cache do
      1000.times { touch_attributes[ARExhibit.find(:all, :limit => 100)] }
    end
  end
 
  x.report('DM.all limit(100) x1,000 (cached)') do
    repository(:default) do
      1000.times { touch_attributes[Exhibit.all(:limit => 100)] }
    end
  end
 
  x.report('AR.all limit(10,000) x10') do
    ActiveRecord::Base::uncached do
      10.times { touch_attributes[ARExhibit.find(:all, :limit => 10_000)] }
    end
  end
 
  x.report('DM.all limit(10,000) x10') do
    10.times { touch_attributes[Exhibit.all(:limit => 10_000)] }
  end
 
  x.report('AR.all limit(10,000) x10 (cached)') do
    ActiveRecord::Base::cache do
      10.times { touch_attributes[ARExhibit.find(:all, :limit => 10_000)] }
    end
  end
 
  x.report('DM.all limit(10,000) x10 (cached)') do
    repository(:default) do
      10.times { touch_attributes[Exhibit.all(:limit => 10_000)] }
    end
  end
 
  # Static, just so AR and DM are on equal footing.
  create_exhibit = {
    :name => Faker::Company.name,
    :zoo_id => rand(10).ceil,
    :notes => Faker::Lorem.paragraphs.join($/),
    :created_on => Date.today,
# :updated_at => Time.now,
  }
 
  x.report('AR.create x10,000') do
    10_000.times { ARExhibit.create(create_exhibit) }
  end
 
  x.report('DM.create x10,000') do
    10_000.times { Exhibit.create(create_exhibit) }
  end
 
  x.report('AR#update x10,000') do
    10_000.times { e = ARExhibit.find(1); e.name = 'bob'; e.save }
  end
 
  x.report('DM#update x10,000') do
    10_000.times { e = Exhibit.get(1); e.name = 'bob'; e.save }
  end
 
  x.report('AR#destroy x10,000') do
    # destroy records 1 to 10,000
    (1..10_000).each { |id| ARExhibit.find(id).destroy }
  end
 
  x.report('DM#destroy x10,000') do
    # destroy records 10,001 to 20,000
    (10_001..20_000).each { |id| Exhibit.get(id).destroy }
  end
end
 
# connection = adapter.create_connection
# command = connection.create_command("DROP TABLE exhibits")
# command.execute_non_query rescue nil
# connection.close
 
__END__
 
On an iMac Core2Duo 2.16GHz:
 
I don't think AR is actually caching.
 
~/src/dm-core > script/performance.rb
Text should not be declared inline.
Rehearsal -----------------------------------------------------------------------------------------------
ActiveRecord:id x10_000 3.530000 0.320000 3.850000 ( 4.939399)
ActiveRecord:id(cached) x10_000 3.540000 0.320000 3.860000 ( 4.797453)
DataMapper:id x10_000 3.190000 0.280000 3.470000 ( 4.604910)
DataMapper:id(cached) x10_000 0.090000 0.000000 0.090000 ( 0.095606)
ActiveRecord:all limit(100) x1000 11.880000 0.060000 11.940000 ( 12.494454)
ActiveRecord:all limit(100) (cached) x1000 11.860000 0.060000 11.920000 ( 12.521918)
DataMapper:all limit(100) x1000 5.020000 0.040000 5.060000 ( 5.578594)
DataMapper:all limit(100) (cached) x1000 3.040000 0.040000 3.080000 ( 3.581179)
ActiveRecord:all limit(10,000) x10 12.040000 0.050000 12.090000 ( 12.243110)
ActiveRecord:all limit(10,000) (cached) x10 12.140000 0.040000 12.180000 ( 12.339861)
DataMapper:all limit(10,000) x10 5.740000 0.040000 5.780000 ( 5.788256)
DataMapper:all limit(10,000) (cached) x10 3.850000 0.020000 3.870000 ( 3.874642)
------------------------------------------------------------------------------------- total: 77.190000sec
 
user system total real
ActiveRecord:id x10_000 3.480000 0.320000 3.800000 ( 4.701110)
ActiveRecord:id(cached) x10_000 3.480000 0.310000 3.790000 ( 4.701436)
DataMapper:id x10_000 3.170000 0.270000 3.440000 ( 4.375630)
DataMapper:id(cached) x10_000 0.100000 0.000000 0.100000 ( 0.097554)
ActiveRecord:all limit(100) x1000 11.410000 0.060000 11.470000 ( 11.993949)
ActiveRecord:all limit(100) (cached) x1000 11.410000 0.050000 11.460000 ( 11.998727)
DataMapper:all limit(100) x1000 5.060000 0.040000 5.100000 ( 5.608384)
DataMapper:all limit(100) (cached) x1000 3.020000 0.040000 3.060000 ( 3.554985)
ActiveRecord:all limit(10,000) x10 12.170000 0.040000 12.210000 ( 12.370468)
ActiveRecord:all limit(10,000) (cached) x10 12.180000 0.040000 12.220000 ( 12.371510)
DataMapper:all limit(10,000) x10 5.450000 0.020000 5.470000 ( 5.480993)
DataMapper:all limit(10,000) (cached) x10 3.130000 0.020000 3.150000 ( 3.160792)
 
On a MacBook Air Core2Duo 1.6GHz (many performance optimizations since the run above)
 
~/src/dm-core > script/performance.rb
Rehearsal -----------------------------------------------------------------------------------------------
AR.id x10,000 11.290000 0.420000 11.710000 ( 14.845046)
DM.id x10,000 5.010000 0.480000 5.490000 ( 8.738212)
AR.id x10,000 (cached) 12.300000 0.520000 12.820000 ( 16.585108)
DM.id x10,000 (cached) 0.450000 0.000000 0.450000 ( 0.474010)
AR.all limit(100) x1,000 3.340000 0.080000 3.420000 ( 4.554041)
DM.all limit(100) x1,000 1.420000 0.050000 1.470000 ( 1.743724)
AR.all limit(100) x1,000 (cached) 3.450000 0.070000 3.520000 ( 3.937980)
DM.all limit(100) x1,000 (cached) 1.070000 0.050000 1.120000 ( 1.390889)
AR.all limit(10,000) x10 0.020000 0.000000 0.020000 ( 0.031253)
DM.all limit(10,000) x10 0.020000 0.000000 0.020000 ( 0.017896)
AR.all limit(10,000) x10 (cached) 0.030000 0.000000 0.030000 ( 0.032688)
DM.all limit(10,000) x10 (cached) 0.010000 0.000000 0.010000 ( 0.013167)
AR.create x10,000 21.390000 1.290000 22.680000 ( 28.519556)
DM.create x10,000 20.090000 0.630000 20.720000 ( 28.822219)
AR.update x10,000 25.450000 1.660000 27.110000 ( 33.040779)
DM.update x10,000 6.550000 0.780000 7.330000 ( 10.467941)
------------------------------------------------------------------------------------ total: 117.920000sec
 
user system total real
AR.id x10,000 11.320000 0.420000 11.740000 ( 13.907577)
DM.id x10,000 4.510000 0.410000 4.920000 ( 6.611278)
AR.id x10,000 (cached) 11.680000 0.440000 12.120000 ( 14.792082)
DM.id x10,000 (cached) 0.380000 0.010000 0.390000 ( 0.384611)
AR.all limit(100) x1,000 25.010000 0.200000 25.210000 ( 25.954076)
DM.all limit(100) x1,000 10.370000 0.090000 10.460000 ( 11.558341)
AR.all limit(100) x1,000 (cached) 28.390000 0.350000 28.740000 ( 30.870503)
DM.all limit(100) x1,000 (cached) 7.760000 0.110000 7.870000 ( 9.211700)
AR.all limit(10,000) x10 27.850000 0.240000 28.090000 ( 28.510220)
DM.all limit(10,000) x10 10.970000 0.040000 11.010000 ( 11.042093)
AR.all limit(10,000) x10 (cached) 29.230000 0.170000 29.400000 ( 29.816547)
DM.all limit(10,000) x10 (cached) 7.470000 0.030000 7.500000 ( 7.520561)
AR.create x10,000 21.700000 1.400000 23.100000 ( 27.188424)
DM.create x10,000 24.380000 0.550000 24.930000 ( 29.439910)
AR.update x10,000 30.670000 1.910000 32.580000 ( 39.227956)
DM.update x10,000 8.290000 0.950000 9.240000 ( 12.912708)