mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
d2c41b1bff
Previously, passing a keyword splat to a method always allocated a hash on the caller side, and accepting arbitrary keywords in a method allocated a separate hash on the callee side. Passing explicit keywords to a method that accepted a keyword splat did not allocate a hash on the caller side, but resulted in two hashes allocated on the callee side. This commit makes passing a single keyword splat to a method not allocate a hash on the caller side. Passing multiple keyword splats or a mix of explicit keywords and a keyword splat still generates a hash on the caller side. On the callee side, if arbitrary keywords are not accepted, it does not allocate a hash. If arbitrary keywords are accepted, it will allocate a hash, but this commit uses a callinfo flag to indicate whether the caller already allocated a hash, and if so, the callee can use the passed hash without duplicating it. So this commit should make it so that a maximum of a single hash is allocated during method calls. To set the callinfo flag appropriately, method call argument compilation checks if only a single keyword splat is given. If only one keyword splat is given, the VM_CALL_KW_SPLAT_MUT callinfo flag is not set, since in that case the keyword splat is passed directly and not mutable. If more than one splat is used, a new hash needs to be generated on the caller side, and in that case the callinfo flag is set, indicating the keyword splat is mutable by the callee. In compile_hash, used for both hash and keyword argument compilation, if compiling keyword arguments and only a single keyword splat is used, pass the argument directly. On the caller side, in vm_args.c, the callinfo flag needs to be recognized and handled. Because the keyword splat argument may not be a hash, it needs to be converted to a hash first if not. Then, unless the callinfo flag is set, the hash needs to be duplicated. The temporary copy of the callinfo flag, kw_flag, is updated if a hash was duplicated, to prevent the need to duplicate it again. If we are converting to a hash or duplicating a hash, we need to update the argument array, which can including duplicating the positional splat array if one was passed. CALLER_SETUP_ARG and a couple other places needs to be modified to handle similar issues for other types of calls. This includes fairly comprehensive tests for different ways keywords are handled internally, checking that you get equal results but that keyword splats on the caller side result in distinct objects for keyword rest parameters. Included are benchmarks for keyword argument calls. Brief results when compiled without optimization: def kw(a: 1) a end def kws(**kw) kw end h = {a: 1} kw(a: 1) # about same kw(**h) # 2.37x faster kws(a: 1) # 1.30x faster kws(**h) # 2.19x faster kw(a: 1, **h) # 1.03x slower kw(**h, **h) # about same kws(a: 1, **h) # 1.16x faster kws(**h, **h) # 1.14x faster |
||
---|---|---|
.. | ||
gc | ||
lib | ||
other-lang | ||
app_answer.rb | ||
app_aobench.rb | ||
app_erb.yml | ||
app_factorial.rb | ||
app_fib.rb | ||
app_lc_fizzbuzz.rb | ||
app_mandelbrot.rb | ||
app_pentomino.rb | ||
app_raise.rb | ||
app_strconcat.rb | ||
app_tak.rb | ||
app_tarai.rb | ||
app_uri.rb | ||
array_flatten.yml | ||
array_intersection.yml | ||
array_sample_100k_10.rb | ||
array_sample_100k_11.rb | ||
array_sample_100k__1k.rb | ||
array_sample_100k__6k.rb | ||
array_sample_100k__100.rb | ||
array_sample_100k___10k.rb | ||
array_sample_100k___50k.rb | ||
array_shift.rb | ||
array_small_and.rb | ||
array_small_diff.rb | ||
array_small_or.rb | ||
array_sort_block.rb | ||
array_sort_float.rb | ||
array_values_at_int.rb | ||
array_values_at_range.rb | ||
bighash.rb | ||
cgi_escape_html.yml | ||
complex_float_add.yml | ||
complex_float_div.yml | ||
complex_float_mul.yml | ||
complex_float_new.yml | ||
complex_float_power.yml | ||
complex_float_sub.yml | ||
dir_empty_p.rb | ||
enum_lazy_grep_v_20.rb | ||
enum_lazy_grep_v_50.rb | ||
enum_lazy_grep_v_100.rb | ||
enum_lazy_uniq_20.rb | ||
enum_lazy_uniq_50.rb | ||
enum_lazy_uniq_100.rb | ||
erb_render.yml | ||
fiber_chain.yml | ||
fiber_locals.yml | ||
file_chmod.rb | ||
file_rename.rb | ||
hash_aref_dsym.rb | ||
hash_aref_dsym_long.rb | ||
hash_aref_fix.rb | ||
hash_aref_flo.rb | ||
hash_aref_miss.rb | ||
hash_aref_str.rb | ||
hash_aref_sym.rb | ||
hash_aref_sym_long.rb | ||
hash_defaults.yml | ||
hash_dup.yml | ||
hash_flatten.rb | ||
hash_ident_flo.rb | ||
hash_ident_num.rb | ||
hash_ident_obj.rb | ||
hash_ident_str.rb | ||
hash_ident_sym.rb | ||
hash_keys.rb | ||
hash_literal_small2.rb | ||
hash_literal_small4.rb | ||
hash_literal_small8.rb | ||
hash_long.rb | ||
hash_shift.rb | ||
hash_shift_u16.rb | ||
hash_shift_u24.rb | ||
hash_shift_u32.rb | ||
hash_small2.rb | ||
hash_small4.rb | ||
hash_small8.rb | ||
hash_to_proc.rb | ||
hash_values.rb | ||
int_quo.rb | ||
io_copy_stream_write.rb | ||
io_copy_stream_write_socket.rb | ||
io_file_create.rb | ||
io_file_read.rb | ||
io_file_write.rb | ||
io_nonblock_noex.rb | ||
io_nonblock_noex2.rb | ||
io_pipe_rw.rb | ||
io_select.rb | ||
io_select2.rb | ||
io_select3.rb | ||
irb_color.yml | ||
irb_exec.yml | ||
kernel_clone.yml | ||
keyword_arguments.yml | ||
loop_for.rb | ||
loop_generator.rb | ||
loop_times.rb | ||
loop_whileloop.rb | ||
loop_whileloop2.rb | ||
marshal_dump_flo.rb | ||
marshal_dump_load_geniv.rb | ||
marshal_dump_load_time.rb | ||
match_gt4.rb | ||
match_small.rb | ||
mjit_exec_jt2jt.yml | ||
mjit_exec_vm2jt.yml | ||
mjit_exec_vm2vm.yml | ||
nil_p.yml | ||
range_last.yml | ||
README.md | ||
realpath.yml | ||
require.yml | ||
require_thread.yml | ||
securerandom.rb | ||
so_ackermann.rb | ||
so_array.rb | ||
so_binary_trees.rb | ||
so_concatenate.rb | ||
so_count_words.yml | ||
so_exception.rb | ||
so_fannkuch.rb | ||
so_fasta.rb | ||
so_k_nucleotide.yml | ||
so_lists.rb | ||
so_mandelbrot.rb | ||
so_matrix.rb | ||
so_meteor_contest.rb | ||
so_nbody.rb | ||
so_nested_loop.rb | ||
so_nsieve.rb | ||
so_nsieve_bits.rb | ||
so_object.rb | ||
so_partial_sums.rb | ||
so_pidigits.rb | ||
so_random.rb | ||
so_reverse_complement.yml | ||
so_sieve.rb | ||
so_spectralnorm.rb | ||
string_capitalize.yml | ||
string_casecmp.yml | ||
string_casecmp_p.yml | ||
string_downcase.yml | ||
string_index.rb | ||
string_scan_re.rb | ||
string_scan_str.rb | ||
string_slice.yml | ||
string_split.yml | ||
string_swapcase.yml | ||
string_upcase.yml | ||
time_strptime.yml | ||
time_subsec.rb | ||
vm1_attr_ivar.yml | ||
vm1_attr_ivar_set.yml | ||
vm1_block.yml | ||
vm1_blockparam.yml | ||
vm1_blockparam_call.yml | ||
vm1_blockparam_pass.yml | ||
vm1_blockparam_yield.yml | ||
vm1_const.yml | ||
vm1_ensure.yml | ||
vm1_float_simple.yml | ||
vm1_gc_short_lived.yml | ||
vm1_gc_short_with_complex_long.yml | ||
vm1_gc_short_with_long.yml | ||
vm1_gc_short_with_symbol.yml | ||
vm1_gc_wb_ary.yml | ||
vm1_gc_wb_ary_promoted.yml | ||
vm1_gc_wb_obj.yml | ||
vm1_gc_wb_obj_promoted.yml | ||
vm1_ivar.yml | ||
vm1_ivar_set.yml | ||
vm1_length.yml | ||
vm1_lvar_init.yml | ||
vm1_lvar_set.yml | ||
vm1_neq.yml | ||
vm1_not.yml | ||
vm1_rescue.yml | ||
vm1_simplereturn.yml | ||
vm1_swap.yml | ||
vm1_yield.yml | ||
vm2_array.yml | ||
vm2_bigarray.yml | ||
vm2_bighash.yml | ||
vm2_case.yml | ||
vm2_case_lit.yml | ||
vm2_defined_method.yml | ||
vm2_dstr.yml | ||
vm2_eval.yml | ||
vm2_fiber_allocate.yml | ||
vm2_fiber_count.yml | ||
vm2_fiber_reuse.yml | ||
vm2_fiber_reuse_gc.yml | ||
vm2_fiber_switch.yml | ||
vm2_freezestring.yml | ||
vm2_method.yml | ||
vm2_method_missing.yml | ||
vm2_method_with_block.yml | ||
vm2_module_ann_const_set.yml | ||
vm2_module_const_set.yml | ||
vm2_mutex.yml | ||
vm2_newlambda.yml | ||
vm2_poly_method.yml | ||
vm2_poly_method_ov.yml | ||
vm2_poly_same_method.yml | ||
vm2_poly_singleton.yml | ||
vm2_proc.yml | ||
vm2_raise1.yml | ||
vm2_raise2.yml | ||
vm2_regexp.yml | ||
vm2_send.yml | ||
vm2_string_literal.yml | ||
vm2_struct_big_aref_hi.yml | ||
vm2_struct_big_aref_lo.yml | ||
vm2_struct_big_aset.yml | ||
vm2_struct_big_href_hi.yml | ||
vm2_struct_big_href_lo.yml | ||
vm2_struct_big_hset.yml | ||
vm2_struct_small_aref.yml | ||
vm2_struct_small_aset.yml | ||
vm2_struct_small_href.yml | ||
vm2_struct_small_hset.yml | ||
vm2_super.yml | ||
vm2_unif1.yml | ||
vm2_zsuper.yml | ||
vm3_backtrace.rb | ||
vm3_clearmethodcache.rb | ||
vm3_gc.rb | ||
vm3_gc_old_full.rb | ||
vm3_gc_old_immediate.rb | ||
vm3_gc_old_lazy.rb | ||
vm_symbol_block_pass.rb | ||
vm_thread_alive_check.yml | ||
vm_thread_close.rb | ||
vm_thread_condvar1.rb | ||
vm_thread_condvar2.rb | ||
vm_thread_create_join.rb | ||
vm_thread_mutex1.rb | ||
vm_thread_mutex2.rb | ||
vm_thread_mutex3.rb | ||
vm_thread_pass.rb | ||
vm_thread_pass_flood.rb | ||
vm_thread_pipe.rb | ||
vm_thread_queue.rb | ||
vm_thread_sized_queue.rb | ||
vm_thread_sized_queue2.rb | ||
vm_thread_sized_queue3.rb | ||
vm_thread_sized_queue4.rb | ||
vm_thread_sleep.yml |
ruby/benchmark
This directory has benchmark definitions to be run with benchmark_driver.gem.
Normal usage
Execute gem install benchmark_driver
and run a command like:
# Run a benchmark script with the ruby in the $PATH
benchmark-driver benchmark/app_fib.rb
# Run benchmark scripts with multiple Ruby executables or options
benchmark-driver benchmark/*.rb -e /path/to/ruby -e '/path/to/ruby --jit'
# Or compare Ruby versions managed by rbenv
benchmark-driver benchmark/*.rb --rbenv '2.5.1;2.6.0-preview2 --jit'
# You can collect many metrics in many ways
benchmark-driver benchmark/*.rb --runner memory --output markdown
# Some are defined with YAML for complex setup or accurate measurement
benchmark-driver benchmark/*.yml
See also:
Usage: benchmark-driver [options] RUBY|YAML...
-r, --runner TYPE Specify runner type: ips, time, memory, once (default: ips)
-o, --output TYPE Specify output type: compare, simple, markdown, record (default: compare)
-e, --executables EXECS Ruby executables (e1::path1 arg1; e2::path2 arg2;...)
--rbenv VERSIONS Ruby executables in rbenv (x.x.x arg1;y.y.y arg2;...)
--repeat-count NUM Try benchmark NUM times and use the fastest result or the worst memory usage
--repeat-result TYPE Yield "best", "average" or "worst" result with --repeat-count (default: best)
--bundler Install and use gems specified in Gemfile
--filter REGEXP Filter out benchmarks with given regexp
--run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3)
-v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2)
make benchmark
Using make benchmark
, make update-benchmark-driver
automatically downloads
the supported version of benchmark_driver, and it runs benchmarks with the downloaded
benchmark_driver.
# Run all benchmarks with the ruby in the $PATH and the built ruby
make benchmark
# Or compare with specific ruby binary
make benchmark COMPARE_RUBY="/path/to/ruby --jit"
# Run vm1 benchmarks
make benchmark ITEM=vm1
# Run some limited benchmarks in ITEM-matched files
make benchmark ITEM=vm1 OPTS=--filter=block
# You can specify the benchmark by an exact filename instead of using the default argument:
# ARGS = $$(find $(srcdir)/benchmark -maxdepth 1 -name '*$(ITEM)*.yml' -o -name '*$(ITEM)*.rb')
make benchmark ARGS=../benchmark/erb_render.yml
# You can specify any option via $OPTS
make benchmark OPTS="--help"
# With `make benchmark`, some special runner plugins are available:
# -r peak, -r size, -r total, -r utime, -r stime, -r cutime, -r cstime
make benchmark ITEM=vm2_bigarray OPTS="-r peak"