Revert cbson.c, to be revisited for malloc/free reduction.

README.md has API Documentation note at the top to help users match documentation to driver version, DOCS-197.
 Expanded performance tests, for insert with multiple docs.
This commit is contained in:
Gary Murakami 2012-05-03 16:43:12 -04:00
parent ca7bf209dd
commit 50d6902d80
6 changed files with 980 additions and 639 deletions

View File

@ -9,6 +9,8 @@ group :development, :test do
# Deployment # Deployment
gem "git" gem "git"
gem "redcarpet"
gem "yard"
# Testing # Testing
gem "mocha" gem "mocha"

View File

@ -1,3 +1,17 @@
# Documentation
This API documentation is available online at [http://api.mongodb.org/ruby](http://api.mongodb.org/ruby)
for all releases of the MongoDB Ruby driver. Please reference the exact version of the documentation
that matches the release of the Ruby driver that you are using. Note that the
[Ruby Language Center for MongoDB](http://www.mongodb.org/display/DOCS/Ruby+Language+Center)
has a link to API Documentation for the current release.
If you have the source, you can generate the matching documentation by typing
$ rake ydoc
Then open the file +ydoc/index.html+.
# Introduction # Introduction
This is the 10gen-supported Ruby driver for [MongoDB](http://www.mongodb.org). This is the 10gen-supported Ruby driver for [MongoDB](http://www.mongodb.org).
@ -300,15 +314,6 @@ If you want to test replica set, you can run the following task:
$ rake test:rs $ rake test:rs
# Documentation
This documentation is available online at [http://api.mongodb.org/ruby](http://api.mongodb.org/ruby). You can
generate the documentation if you have the source by typing
$ rake ydoc
Then open the file +ydoc/index.html+.
# Release Notes # Release Notes
See HISTORY. See HISTORY.

View File

@ -18,13 +18,18 @@
width:800px; width:800px;
height:400px; height:400px;
} }
div.hidden {
//display: none; //visibility: hidden; // to collapse, use display: none
}
</style> </style>
</head> </head>
<body> <body>
<h1>Exp Series Performance Tests</h1> <h1>Exp Series Performance Tests</h1>
x-axis is power of 2, log base 2 of size<br> x-axis is power of 2, log base 2 of size<br>
y-axis is operations per user-time CPU-second<br> y-axis is "document" operations per second, ex., total document insertions per second<br>
Note that this is not operations per real-time second that include DB real-time<br> <br>
For measuring Ruby driver performance, we are interested primarily in the "user" CPU time.<br>
The "user" time is the time used by the Ruby driver, typically much less than real time.<br>
<div id="placeholder"></div> <div id="placeholder"></div>
<script type="text/javascript"> <script type="text/javascript">
@ -42,7 +47,7 @@
return $.map(plotSpecs, function(plotSpec, i){ return $.map(plotSpecs, function(plotSpec, i){
return { return {
label: labelSpec + ': ' + plotSpec[labelSpec], label: labelSpec + ': ' + plotSpec[labelSpec],
data: genOpXY(expSeries, xMax, plotSpec, 'exp2', 'ops'), data: genOpXY(expSeries, xMax, plotSpec, 'exp2', 'ut_ops'),
lines: { show: true }, lines: { show: true },
points: { show: true } points: { show: true }
}; };
@ -58,106 +63,108 @@ $(function () {
} }
return res; return res;
} }
function doPlot(title, series) { function doPlot(title, series, classes) {
var id = title.replace(/\W/g,'_'); var id = title.replace(/\W/g,'_');
$("#placeholder").append('<h1>' + title + '</h1><div id="' + id + '" class="graph"></div>'); $("#placeholder").append('<h3>' + title + '</h3><div id="show_hide_' + id + '" class="show_hide">Show/Hide</div><div id="' + id + '" class="graph"></div>');
$.plot($('#' + id), var e = $('#' + id);
series, $.plot(e, series, {
{
xaxis:{ ticks:xExpTicks }, xaxis:{ ticks:xExpTicks },
yaxes: [ { min: 0 } ], yaxes:[
{ min:0 }
],
legend:{ position:'ne' }, legend:{ position:'ne' },
grid:{ hoverable:true } grid:{ hoverable:true }
}); });
e.addClass(classes);
} }
// comment pending // comment pending
var graph = [ var graph = [
[ 'value_string_size insert C versus Ruby', 14, 'mode', [ '#placeholder', 'value_string_size insert C versus Ruby', 'hidden', 14, 'mode',
[ [
{ base:2, generator:'value_string_size', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'value_string_size', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'value_string_size', operation:'insert', mode: 'ruby', tag: 'orig_ruby' } { base:2, generator:'value_string_size', operation:'insert', mode: 'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'key_string_size insert C versus Ruby', 14, 'mode', [ '#placeholder', 'key_string_size insert C versus Ruby', 'hidden', 14, 'mode',
[ [
{ base:2, generator:'key_string_size', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'key_string_size', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'key_string_size', operation:'insert', mode: 'ruby', tag: 'orig_ruby' } { base:2, generator:'key_string_size', operation:'insert', mode: 'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'array_size_fixnum insert C versus Ruby', 12, 'mode', [ '#placeholder', 'array_size_fixnum insert C versus Ruby', 'hidden', 12, 'mode',
[ [
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'ruby', tag: 'orig_ruby' } { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'hash_size_fixnum insert C versus Ruby', 12, 'mode', [ '#placeholder', 'hash_size_fixnum insert C versus Ruby', 'hidden', 12, 'mode',
[ [
{ base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'ruby', tag: 'orig_ruby' } { base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'array_nest_fixnum base 2 insert C versus Ruby', 12, 'mode', [ '#placeholder', 'array_nest_fixnum base 2 insert C versus Ruby', 'hidden', 12, 'mode',
[ [
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode:'c', tag: 'array_slow' }, { base:2, generator:'array_nest_fixnum', operation:'insert', mode:'c', tag: 'base_c' },
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode:'ruby', tag: 'orig_ruby' } { base:2, generator:'array_nest_fixnum', operation:'insert', mode:'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'hash_nest_fixnum base 2 insert C versus Ruby', 12, 'mode', [ '#placeholder', 'hash_nest_fixnum base 2 insert C versus Ruby', 'hidden', 12, 'mode',
[ [
{ base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'ruby', tag: 'orig_ruby' } { base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'ruby', tag: 'base_ruby' }
] ]
], ],
[ 'array_nest_fixnum insert C by base', 12, 'base', [ '#placeholder', 'array_nest_fixnum insert C by base', 'hidden', 12, 'base',
[ [
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:4, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:4, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:8, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:8, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:16, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:16, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:32, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' } { base:32, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' }
] ]
], ],
[ 'hash_nest_fixnum insert C by base', 12, 'base', [ '#placeholder', 'hash_nest_fixnum insert C by base', 'hidden', 12, 'base',
[ [
{ base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:4, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:4, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:8, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:8, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:16, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:16, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' },
{ base:32, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' } { base:32, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c_nest_full' }
] ]
], ],
[ 'array_size_fixnum slow versus hash_size_fixnum insert C', 12, 'generator', [ '#placeholder', 'array_size_fixnum slow versus hash_size_fixnum insert C', '', 12, 'generator',
[ [
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' } { base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' }
] ]
], ],
[ 'array_size_fixnum slow versus fast insert C', 12, 'tag', [ '#placeholder', 'array_size_fixnum slow versus fast insert C', 'hidden', 12, 'tag',
[ [
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' } { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' }
] ]
], ],
[ 'array_size_fixnum fast versus hash_size_fixnum insert C', 12, 'generator', [ '#placeholder', 'array_size_fixnum fast versus hash_size_fixnum insert C', 'hidden', 12, 'generator',
[ [
{ base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' }, { base:2, generator:'array_size_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' },
{ base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' } { base:2, generator:'hash_size_fixnum', operation:'insert', mode: 'c', tag: 'base_c' }
] ]
], ],
[ 'array_nest_fixnum slow versus hash_nest_fixnum insert C base 2', 12, 'generator', [ '#placeholder', 'array_nest_fixnum slow versus hash_nest_fixnum insert C base 2', 'hidden', 12, 'generator',
[ [
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' } { base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c' }
] ]
], ],
[ 'array_nest_fixnum slow versus fast insert C base 2', 12, 'tag', [ '#placeholder', 'array_nest_fixnum slow versus fast insert C base 2', 'hidden', 12, 'tag',
[ [
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_slow' }, { base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'base_c' },
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' } { base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' }
] ]
], ],
[ 'array_nest_fixnum fast versus hash_nest_fixnum insert C base 2', 12, 'generator', [ '#placeholder', 'array_nest_fixnum fast versus hash_nest_fixnum insert C base 2', 'hidden', 12, 'generator',
[ [
{ base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' }, { base:2, generator:'array_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' },
{ base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' } { base:2, generator:'hash_nest_fixnum', operation:'insert', mode: 'c', tag: 'array_fast' }
@ -165,10 +172,11 @@ $(function () {
], ],
]; ];
$.each(graph, function(i, e){ $.each(graph, function(i, e){
var title, xMax, labelSpec, plotSpecs; var section, title, classes, xMax, labelSpec, plotSpecs;
title = e[0]; xMax = e[1]; labelSpec = e[2]; plotSpecs = e[3]; //[title, xMax, labelSpec, plotSpecs] = e; //[section, title, classes, xMax, labelSpec, plotSpecs] = e;
section = e[0], title = e[1]; classes = e[2], xMax = e[3]; labelSpec = e[4]; plotSpecs = e[5];
var series = flotSeries(expSeries, xMax, labelSpec, plotSpecs); var series = flotSeries(expSeries, xMax, labelSpec, plotSpecs);
doPlot(title, series); doPlot(title, series, classes);
}); });
function showTooltip(x, y, contents) { function showTooltip(x, y, contents) {
@ -206,6 +214,11 @@ $(function () {
} }
}); });
$('.show_hide').bind('click', function(event) {
var id = $(this).attr('id').replace(/^show_hide_/, '');
$('#' + id).toggleClass('hidden');
});
}); });
</script> </script>
</body> </body>

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@ $calibration_runtime = 0.1
$target_runtime = 5.0 $target_runtime = 5.0
$db_name = 'benchmark' $db_name = 'benchmark'
$collection_name = 'exp_series' $collection_name = 'exp_series'
$mode = set_mode('ruby') $mode = set_mode('c')
$hostname = `uname -n`[/([^.]*)/,1] $hostname = `uname -n`[/([^.]*)/,1]
$osname = `uname -s`.strip $osname = `uname -s`.strip
$tag = `git log -1 --format=oneline`.split[0] $tag = `git log -1 --format=oneline`.split[0]
@ -34,7 +34,7 @@ $date = Time.now.strftime('%Y%m%d-%H%M')
options_with_help = [ options_with_help = [
[ '--help', '-h', GetoptLong::NO_ARGUMENT, '', 'show help' ], [ '--help', '-h', GetoptLong::NO_ARGUMENT, '', 'show help' ],
[ '--mode', '-m', GetoptLong::OPTIONAL_ARGUMENT, ' mode', 'set mode to "c" or "ruby" (default)' ], [ '--mode', '-m', GetoptLong::OPTIONAL_ARGUMENT, ' mode', 'set mode to "c" or "ruby" (c)' ],
[ '--tag', '-t', GetoptLong::OPTIONAL_ARGUMENT, ' tag', 'set tag for run, default is git commit key' ] [ '--tag', '-t', GetoptLong::OPTIONAL_ARGUMENT, ' tag', 'set tag for run, default is git commit key' ]
] ]
options = options_with_help.collect{|option|option[0...3]} options = options_with_help.collect{|option|option[0...3]}
@ -88,13 +88,6 @@ class TestExpPerformance < Test::Unit::TestCase
return Array.new(base, array_nest(base, level - 1, obj)) return Array.new(base, array_nest(base, level - 1, obj))
end end
def hash_nest(base, level, obj)
return obj if level == 0
h = Hash.new
(0...base).each{|i| h[i.to_s] = hash_nest(base, level - 1, obj)}
return h
end
def test__array_nest def test__array_nest
assert_equal(1, array_nest(2,0,1)) assert_equal(1, array_nest(2,0,1))
assert_equal([1, 1], array_nest(2,1,1)) assert_equal([1, 1], array_nest(2,1,1))
@ -107,6 +100,13 @@ class TestExpPerformance < Test::Unit::TestCase
assert_equal([1, 1, 1, 1, 1, 1, 1, 1], array_nest(8,1,1)) assert_equal([1, 1, 1, 1, 1, 1, 1, 1], array_nest(8,1,1))
end end
def hash_nest(base, level, obj)
return obj if level == 0
h = Hash.new
(0...base).each{|i| h[i.to_s] = hash_nest(base, level - 1, obj)}
return h
end
def test__hash_nest def test__hash_nest
assert_equal(1, hash_nest(2, 0, 1)) assert_equal(1, hash_nest(2, 0, 1))
assert_equal({"0"=>1, "1"=>1}, hash_nest(2, 1, 1)) assert_equal({"0"=>1, "1"=>1}, hash_nest(2, 1, 1))
@ -123,6 +123,25 @@ class TestExpPerformance < Test::Unit::TestCase
assert_equal({"0"=>1, "1"=>1, "2"=>1, "3"=>1, "4"=>1, "5"=>1, "6"=>1, "7"=>1}, hash_nest(8,1,1)) assert_equal({"0"=>1, "1"=>1, "2"=>1, "3"=>1, "4"=>1, "5"=>1, "6"=>1, "7"=>1}, hash_nest(8,1,1))
end end
def multi_doc(multi_power, doc)
return doc if multi_power == -1
return (2 ** multi_power).times.collect{doc.dup}
end
def test_multi_doc
doc = {'a' => 1}
assert_equal({"a"=>1}, multi_doc(-1, doc))
assert_equal([{"a"=>1}], multi_doc(0, doc))
assert_equal([{"a"=>1}, {"a"=>1}], multi_doc(1, doc))
assert_equal([{"a"=>1}, {"a"=>1}, {"a"=>1}, {"a"=>1}], multi_doc(2, doc))
assert_equal(8, multi_doc(3, doc).size)
assert_equal(16, multi_doc(4, doc).size)
assert_equal(32, multi_doc(5, doc).size)
mdoc = multi_doc(2, doc)
mdoc[0]['b'] = 2
assert_equal([{"a"=>1, "b"=>2}, {"a"=>1}, {"a"=>1}, {"a"=>1}], mdoc, 'non-dup doc will fail for insert many safe')
end
# Performance Tuning Engineering # Performance Tuning Engineering
## Completed ## Completed
### How to measure and compare pure Ruby versus C extension performance ### How to measure and compare pure Ruby versus C extension performance
@ -167,7 +186,7 @@ class TestExpPerformance < Test::Unit::TestCase
# consider inserting the results into a database collection # consider inserting the results into a database collection
# Test::Unit::TestCase pollutes STDOUT, so write to a file # Test::Unit::TestCase pollutes STDOUT, so write to a file
File.open("exp_series-#{$date}-#{$tag}.js", 'w+'){|f| File.open("exp_series-#{$date}-#{$tag}.js", 'w+'){|f|
f.puts("#{@results.to_json.gsub(/\[/, "").gsub(/(}[\],])/, "},\n")}") unless @results.empty? f.puts(@results.to_json.gsub(/\[/, "").gsub(/}[\],]/, "},\n")) unless @results.empty?
} }
@conn.drop_database($db_name) @conn.drop_database($db_name)
end end
@ -206,23 +225,34 @@ class TestExpPerformance < Test::Unit::TestCase
return [iterations, utime, rtime, etime] return [iterations, utime, rtime, etime]
end end
def power_test(base, max_power, db, coll, generator, setup, operation, teardown) def power_test(args)
base, max_power, multi, generator, setup, operation, teardown = args
generator, setup, operation, teardown = method(generator), method(setup), method(operation), method(teardown)
return (0..max_power).collect do |power| return (0..max_power).collect do |power|
multi_start, multi_end = (multi == -1) ? [-1, -1] : [0, multi]
(multi_start..multi_end).collect do |multi_power|
size, doc = generator.call(base, power) size, doc = generator.call(base, power)
iterations, utime, rtime, etime = valuate(db, coll, doc, setup, teardown) { operation.call(coll, doc) } doc = multi_doc(multi_power, doc)
multi_size = (doc.class == Array) ? doc.size : 1;
iterations, utime, rtime, etime = valuate(@db, @coll, doc, setup, teardown) { operation.call(@coll, doc) }
multi_iterations = multi_size.to_f * iterations.to_f
result = { result = {
'base' => base, 'base' => base,
'power' => power, 'power' => power,
'size' => size, 'size' => size,
'exp2' => Math.log2(size).to_i, 'exp2' => Math.log2(size).to_i,
'multi_power' => multi_power,
'multi_size' => multi_size,
'generator' => generator.name.to_s, 'generator' => generator.name.to_s,
'operation' => operation.name.to_s, 'operation' => operation.name.to_s,
'iterations' => iterations, 'iterations' => iterations,
'utime' => utime.round(2), 'utime' => utime.round(2),
'etime' => etime.round(2),
'rtime' => rtime.round(2), 'rtime' => rtime.round(2),
'ops' => (iterations.to_f / utime.to_f).round(1), 'ut_ops' => (multi_iterations / utime.to_f).round(1),
'usec' => (1000000.0 * utime.to_f / iterations.to_f).round(1), 'rt_ops' => (multi_iterations / rtime.to_f).round(1),
'ut_usec' => (1000000.0 * utime.to_f / multi_iterations).round(1),
'rt_usec' => (1000000.0 * rtime.to_f / multi_iterations).round(1),
'etime' => etime.round(2),
'mode' => $mode, 'mode' => $mode,
'hostname' => $hostname, 'hostname' => $hostname,
'osname' => $osname, 'osname' => $osname,
@ -234,6 +264,7 @@ class TestExpPerformance < Test::Unit::TestCase
STDERR.flush STDERR.flush
result result
end end
end.flatten
end end
def value_string_size(base, power) def value_string_size(base, power)
@ -277,19 +308,27 @@ class TestExpPerformance < Test::Unit::TestCase
end end
def cursor_setup(db, coll, doc, iterations) def cursor_setup(db, coll, doc, iterations)
(0...(iterations - coll.size)).each{insert(coll, doc)} (0...(iterations - coll.size)).each{insert(coll, doc)} #TODO - insert many
@cursor = coll.find @cursor = coll.find
@queries = 1 @queries = 1
end end
def clear_ids(doc) # delete :_id to really insert, required for safe
if doc.class == Array
doc.each{|d|d.delete(:_id)}
else
doc.delete(:_id)
end
end
def insert(coll, doc) def insert(coll, doc)
doc.delete(:_id) # delete :_id to insert clear_ids(doc)
coll.insert(doc) # note that insert stores :_id in doc and subsequent inserts are updates coll.insert(doc) # note that insert stores :_id in doc and subsequent inserts are updates
end end
def insert_safe(coll, doc) def insert_safe(coll, doc)
doc.delete(:_id) # delete :_id to insert clear_ids(doc)
coll.insert(doc, :safe => true) # note that insert stores :_id in doc and subsequent inserts are updates coll.insert(doc, :safe => true) # note that insert stores :_id in doc and subsequent inserts with :_id are updates
end end
def cursor_next(coll, doc) def cursor_next(coll, doc)
@ -317,123 +356,99 @@ class TestExpPerformance < Test::Unit::TestCase
end end
def test_insert def test_insert
tests = [ [
[2, 15, :value_string_size, :null_setup, :insert, :default_teardown], [2, 15, -1, :value_string_size, :null_setup, :insert, :default_teardown],
[2, 15, :key_string_size, :null_setup, :insert, :default_teardown], [2, 15, -1, :key_string_size, :null_setup, :insert, :default_teardown],
[2, 14, :array_size_fixnum, :null_setup, :insert, :default_teardown], [2, 14, -1, :array_size_fixnum, :null_setup, :insert, :default_teardown],
[2, 17, :hash_size_fixnum, :null_setup, :insert, :default_teardown], [2, 17, -1, :hash_size_fixnum, :null_setup, :insert, :default_teardown],
[2, 12, :array_nest_fixnum, :null_setup, :insert, :default_teardown], [2, 12, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[4, 6, :array_nest_fixnum, :null_setup, :insert, :default_teardown], [2, 15, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[8, 4, :array_nest_fixnum, :null_setup, :insert, :default_teardown], ].each{|args| @results += power_test(args)}
[16, 3, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[32, 2, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[2, 15, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[4, 8, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[8, 4, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[16, 4, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[32, 3, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
]
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end end
def test_insert_nest_full
[
[2, 12, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[4, 6, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[8, 4, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[16, 3, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[32, 2, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[2, 15, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[4, 8, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[8, 4, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[16, 4, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
[32, 3, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
].each{|args| @results += power_test(args)}
end
def test_array_fast
[
[2, 14, -1, :array_size_fixnum, :null_setup, :insert, :default_teardown],
[2, 17, -1, :hash_size_fixnum, :null_setup, :insert, :default_teardown],
[2, 12, -1, :array_nest_fixnum, :null_setup, :insert, :default_teardown],
[2, 15, -1, :hash_nest_fixnum, :null_setup, :insert, :default_teardown],
].each{|args| @results += power_test(args)}
end end
def test_insert_safe def test_insert_safe
tests = [ [
[2, 15, :value_string_size, :null_setup, :insert_safe, :default_teardown], [2, 15, -1, :value_string_size, :null_setup, :insert_safe, :default_teardown],
[2, 15, :key_string_size, :null_setup, :insert_safe, :default_teardown], [2, 15, -1, :key_string_size, :null_setup, :insert_safe, :default_teardown],
[2, 14, :array_size_fixnum, :null_setup, :insert_safe, :default_teardown], [2, 14, -1, :array_size_fixnum, :null_setup, :insert_safe, :default_teardown],
[2, 17, :hash_size_fixnum, :null_setup, :insert_safe, :default_teardown], [2, 17, -1, :hash_size_fixnum, :null_setup, :insert_safe, :default_teardown],
[2, 12, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown], [2, 12, -1, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[4, 6, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown], [2, 15, -1, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[8, 4, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown], ].each{|args| @results += power_test(args)}
[16, 3, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[32, 2, :array_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[2, 15, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[4, 8, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[8, 4, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[16, 4, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
[32, 3, :hash_nest_fixnum, :null_setup, :insert_safe, :default_teardown],
]
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end
end end
def xtest_insert_many def test_insert_many
[
[2, 2, 10, :value_string_size, :null_setup, :insert, :default_teardown],
[2, 14, 10, :hash_size_fixnum, :null_setup, :insert, :default_teardown],
[2, 14, 10, :array_size_fixnum, :null_setup, :insert, :default_teardown],
].each{|args| @results += power_test(args)}
end end
def xtest_insert_many_safe def test_insert_many_safe
[
[2, 2, 10, :value_string_size, :null_setup, :insert_safe, :default_teardown],
[2, 14, 10, :hash_size_fixnum, :null_setup, :insert_safe, :default_teardown],
[2, 14, 10, :array_size_fixnum, :null_setup, :insert_safe, :default_teardown],
].each{|args| @results += power_test(args)}
end end
def test_find def test_find
tests = [ [
[2, 15, :value_string_size, :cursor_setup, :cursor_next, :cursor_teardown], [2, 15, -1, :value_string_size, :cursor_setup, :cursor_next, :cursor_teardown],
[2, 15, :key_string_size, :cursor_setup, :cursor_next, :cursor_teardown], [2, 15, -1, :key_string_size, :cursor_setup, :cursor_next, :cursor_teardown],
[2, 14, :array_size_fixnum, :cursor_setup, :cursor_next, :cursor_teardown], [2, 14, -1, :array_size_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[2, 17, :hash_size_fixnum, :cursor_setup, :cursor_next, :cursor_teardown], [2, 17, -1, :hash_size_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[2, 12, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown], [2, 12, -1, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[4, 6, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown], [2, 15, -1, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[8, 4, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown], ].each{|args| @results += power_test(args)}
[16, 3, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[32, 2, :array_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[2, 15, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[4, 8, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[8, 4, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[16, 4, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
[32, 3, :hash_nest_fixnum, :cursor_setup, :cursor_next, :cursor_teardown],
]
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end
end end
def test_find_one def test_find_one
tests = [ [
[2, 15, :value_string_size, :find_one_setup, :find_one, :default_teardown], [2, 15, -1, :value_string_size, :find_one_setup, :find_one, :default_teardown],
[2, 15, :key_string_size, :find_one_setup, :find_one, :default_teardown], [2, 15, -1, :key_string_size, :find_one_setup, :find_one, :default_teardown],
[2, 14, :array_size_fixnum, :find_one_setup, :find_one, :default_teardown], [2, 14, -1, :array_size_fixnum, :find_one_setup, :find_one, :default_teardown],
[2, 17, :hash_size_fixnum, :find_one_setup, :find_one, :default_teardown], [2, 17, -1, :hash_size_fixnum, :find_one_setup, :find_one, :default_teardown],
[2, 12, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown], [2, 12, -1, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[4, 6, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown], [2, 15, -1, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[8, 4, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown], ].each{|args| @results += power_test(args)}
[16, 3, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[32, 2, :array_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[2, 15, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[4, 8, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[8, 4, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[16, 4, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
[32, 3, :hash_nest_fixnum, :find_one_setup, :find_one, :default_teardown],
]
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end
end end
def xtest_update def xtest_update
tests = [ [
# pending # pending
] ].each{|args| @results += power_test(args)}
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end
end end
def xtest_remove def xtest_remove
tests = [ [
# pending # pending
] ].each{|args| @results += power_test(args)}
tests.each do |base, max_power, generator, setup, operation, teardown|
# consider moving 'method' as permitted by scope
@results += power_test(base, max_power, @db, @coll, method(generator), method(setup), method(operation), method(teardown))
end
end end
# synthesized mix, real-world data pending # synthesized mix, real-world data pending

View File

@ -139,14 +139,38 @@ static void write_utf8(bson_buffer_t buffer, VALUE string, char check_null) {
#define EXTENDED RE_OPTION_EXTENDED #define EXTENDED RE_OPTION_EXTENDED
#endif #endif
#define ARRAY_KEY_BUFFER_SIZE 10 /* TODO we ought to check that the malloc or asprintf was successful
// use 8^(ARRAY_KEY_BUFFER_SIZE-1) as CPP safe bounds approximation for limit of 10^(ARRAY_KEY_BUFFER_SIZE-1)-1 * and raise an exception if not. */
#define ARRAY_KEY_MAX_CPP (1 << (3 * (ARRAY_KEY_BUFFER_SIZE-1))) /* TODO maybe we can use something more portable like vsnprintf instead
* of this hack. And share it with the Python extension ;) */
/* If we don't have ASPRINTF, there are two possibilities:
* either use _scprintf and _snprintf on for Windows or
* use snprintf for solaris. */
#ifndef HAVE_ASPRINTF
#ifdef _WIN32 || _MSC_VER #ifdef _WIN32 || _MSC_VER
#define SNPRINTF _snprintf #define INT2STRING(buffer, i) \
{ \
int vslength = _scprintf("%d", i) + 1; \
*buffer = malloc(vslength); \
_snprintf(*buffer, vslength, "%d", i); \
}
#define FREE_INTSTRING(buffer) free(buffer)
#else #else
#define SNPRINTF snprintf #define INT2STRING(buffer, i) \
{ \
int vslength = snprintf(NULL, 0, "%d", i) + 1; \
*buffer = malloc(vslength); \
snprintf(*buffer, vslength, "%d", i); \
}
#define FREE_INTSTRING(buffer) free(buffer)
#endif
#else
#define INT2STRING(buffer, i) asprintf(buffer, "%d", i);
#ifdef USING_SYSTEM_ALLOCATOR_LIBRARY /* Ruby Enterprise Edition with tcmalloc */
#define FREE_INTSTRING(buffer) system_free(buffer)
#else
#define FREE_INTSTRING(buffer) free(buffer)
#endif
#endif #endif
#ifndef RREGEXP_SRC #ifndef RREGEXP_SRC
@ -277,7 +301,6 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
bson_buffer_position length_location, start_position, obj_length; bson_buffer_position length_location, start_position, obj_length;
int items, i; int items, i;
VALUE* values; VALUE* values;
char name[ARRAY_KEY_BUFFER_SIZE];
write_name_and_type(buffer, key, 0x04); write_name_and_type(buffer, key, 0x04);
start_position = bson_buffer_get_position(buffer); start_position = bson_buffer_get_position(buffer);
@ -289,14 +312,15 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
} }
items = RARRAY_LENINT(value); items = RARRAY_LENINT(value);
if (items > ARRAY_KEY_MAX_CPP)
rb_raise(rb_eTypeError, "array size too large");
for(i = 0; i < items; i++) { for(i = 0; i < items; i++) {
char* name;
VALUE key; VALUE key;
SNPRINTF(name, ARRAY_KEY_BUFFER_SIZE, "%d", i); INT2STRING(&name, i);
key = rb_str_new2(name); key = rb_str_new2(name);
write_element_with_id(key, rb_ary_entry(value, i), pack_extra(buffer, check_keys)); write_element_with_id(key, rb_ary_entry(value, i), pack_extra(buffer, check_keys));
FREE_INTSTRING(name);
} }
// write null byte and fill in length // write null byte and fill in length
SAFE_WRITE(buffer, &zero, 1); SAFE_WRITE(buffer, &zero, 1);
obj_length = bson_buffer_get_position(buffer) - start_position; obj_length = bson_buffer_get_position(buffer) - start_position;