aboutsummaryrefslogtreecommitdiffstats
path: root/vm_callinfo.h
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2020-02-24 12:05:07 -0800
committerJeremy Evans <code@jeremyevans.net>2020-03-17 12:09:43 -0700
commitd2c41b1bff1f3102544bb0d03d4e82356d034d33 (patch)
tree468467ff804e7deb9eca40b7d022c996e929785f /vm_callinfo.h
parentac04b778c12120ab91986822b71edf16fea61465 (diff)
downloadruby-d2c41b1bff1f3102544bb0d03d4e82356d034d33.tar.gz
Reduce allocations for keyword argument hashes
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
Diffstat (limited to 'vm_callinfo.h')
-rw-r--r--vm_callinfo.h6
1 files changed, 4 insertions, 2 deletions
diff --git a/vm_callinfo.h b/vm_callinfo.h
index 30c4f703ba..f99f808a83 100644
--- a/vm_callinfo.h
+++ b/vm_callinfo.h
@@ -13,6 +13,7 @@ enum vm_call_flag_bits {
VM_CALL_SUPER_bit, /* super */
VM_CALL_ZSUPER_bit, /* zsuper */
VM_CALL_OPT_SEND_bit, /* internal flag */
+ VM_CALL_KW_SPLAT_MUT_bit, /* kw splat hash can be modified (to avoid allocating a new one) */
VM_CALL__END
};
@@ -28,6 +29,7 @@ enum vm_call_flag_bits {
#define VM_CALL_SUPER (0x01 << VM_CALL_SUPER_bit)
#define VM_CALL_ZSUPER (0x01 << VM_CALL_ZSUPER_bit)
#define VM_CALL_OPT_SEND (0x01 << VM_CALL_OPT_SEND_bit)
+#define VM_CALL_KW_SPLAT_MUT (0x01 << VM_CALL_KW_SPLAT_MUT_bit)
struct rb_callinfo_kwarg {
int keyword_len;
@@ -64,8 +66,8 @@ struct rb_callinfo {
#define CI_EMBED_ID_bits 32
#elif SIZEOF_VALUE == 4
#define CI_EMBED_TAG_bits 1
-#define CI_EMBED_ARGC_bits 4
-#define CI_EMBED_FLAG_bits 12
+#define CI_EMBED_ARGC_bits 3
+#define CI_EMBED_FLAG_bits 13
#define CI_EMBED_ID_bits 15
#endif