From 226ed1a741fd6a701260cd138bc59acb7e9d49c0 Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Fri, 19 Sep 2014 17:42:12 +0000
Subject: [PATCH] rb_method_definition_t becomes 32 bytes (from 40) on 64-bit

Storing the function pointer inline with rb_method_cfunc_t bloated
rb_method_definition_t for all functions, leading to wasted space.
We can store the cfunc invoker function pointers in a static table
and look up the function quickly by calculating the offset with
argc.

rb_method_definition_t is now 32 bytes on a 64-bit system and
may be cache-aligned in the future to occupy half a 64-byte cache line.

Saves around 20K of allocations at startup with "valgrind ruby -e exit"
before:
  total heap usage: 49,445 allocs, 20,027 frees, 8,492,836 bytes allocated
after:
  total heap usage: 49,445 allocs, 20,040 frees, 8,472,372 bytes allocated
---
 method.h        |  3 ++-
 vm_eval.c       |  4 ++--
 vm_insnhelper.c | 30 ++++++++++++++++++++++++++++--
 vm_method.c     | 30 ++----------------------------
 4 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/method.h b/method.h
index 8267809..2f952f4 100644
--- a/method.h
+++ b/method.h
@@ -64,10 +64,11 @@ struct rb_call_info_struct;
 
 typedef struct rb_method_cfunc_struct {
     VALUE (*func)(ANYARGS);
-    VALUE (*invoker)(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv);
     int argc;
 } rb_method_cfunc_t;
 
+typedef VALUE (*rb_cfunc_invoker_t)(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *);
+
 typedef struct rb_method_attr_struct {
     ID id;
     const VALUE location;
diff --git a/vm_eval.c b/vm_eval.c
index 5cb7293..afc1379 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -73,7 +73,7 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
 	th->passed_ci = ci;
 	ci->aux.inc_sp = 0;
 	VM_PROFILE_UP(2);
-	val = (*cfunc->invoker)(cfunc->func, ci, argv);
+	val = (rb_cfunc_invoker(cfunc))(cfunc->func, ci, argv);
 
 	if (reg_cfp == th->cfp) {
 	    if (UNLIKELY(th->passed_ci != ci)) {
@@ -119,7 +119,7 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv
 	if (len >= 0) rb_check_arity(argc, len, len);
 
 	VM_PROFILE_UP(2);
-	val = (*cfunc->invoker)(cfunc->func, recv, argc, argv);
+	val = (rb_cfunc_invoker(cfunc))(cfunc->func, recv, argc, argv);
 
 	if (UNLIKELY(reg_cfp != th->cfp + 1)) {
 		rb_bug("vm_call0_cfunc_with_frame: cfp consistency error");
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index f57ba9c..516081a 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1447,6 +1447,32 @@ call_cfunc_15(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv)
     return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]);
 }
 
+static rb_cfunc_invoker_t
+rb_cfunc_invoker(const rb_method_cfunc_t *cfunc)
+{
+    static const rb_cfunc_invoker_t const tbl[] = {
+	call_cfunc_m2,
+	call_cfunc_m1,
+	call_cfunc_0,
+	call_cfunc_1,
+	call_cfunc_2,
+	call_cfunc_3,
+	call_cfunc_4,
+	call_cfunc_5,
+	call_cfunc_6,
+	call_cfunc_7,
+	call_cfunc_8,
+	call_cfunc_9,
+	call_cfunc_10,
+	call_cfunc_11,
+	call_cfunc_12,
+	call_cfunc_13,
+	call_cfunc_14,
+	call_cfunc_15
+    };
+    return tbl[cfunc->argc + 2];
+}
+
 #ifndef VM_PROFILE
 #define VM_PROFILE 0
 #endif
@@ -1520,7 +1546,7 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i
 
     reg_cfp->sp -= argc + 1;
     VM_PROFILE_UP(0);
-    val = (*cfunc->invoker)(cfunc->func, recv, argc, reg_cfp->sp + 1);
+    val = (rb_cfunc_invoker(cfunc))(cfunc->func, recv, argc, reg_cfp->sp + 1);
 
     if (reg_cfp != th->cfp + 1) {
 	rb_bug("vm_call_cfunc - cfp consistency error");
@@ -1547,7 +1573,7 @@ vm_call_cfunc_latter(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_
     reg_cfp->sp -= argc + 1;
     ci->aux.inc_sp = argc + 1;
     VM_PROFILE_UP(0);
-    val = (*cfunc->invoker)(cfunc->func, ci, argv);
+    val = (rb_cfunc_invoker(cfunc))(cfunc->func, ci, argv);
 
     /* check */
     if (reg_cfp == th->cfp) { /* no frame push */
diff --git a/vm_method.c b/vm_method.c
index ffcf5be..98bd50d 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -412,39 +412,13 @@ method_added(VALUE klass, ID mid)
     }
 }
 
-static VALUE
-(*call_cfunc_invoker_func(int argc))(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *)
-{
-    switch (argc) {
-      case -2: return &call_cfunc_m2;
-      case -1: return &call_cfunc_m1;
-      case 0: return &call_cfunc_0;
-      case 1: return &call_cfunc_1;
-      case 2: return &call_cfunc_2;
-      case 3: return &call_cfunc_3;
-      case 4: return &call_cfunc_4;
-      case 5: return &call_cfunc_5;
-      case 6: return &call_cfunc_6;
-      case 7: return &call_cfunc_7;
-      case 8: return &call_cfunc_8;
-      case 9: return &call_cfunc_9;
-      case 10: return &call_cfunc_10;
-      case 11: return &call_cfunc_11;
-      case 12: return &call_cfunc_12;
-      case 13: return &call_cfunc_13;
-      case 14: return &call_cfunc_14;
-      case 15: return &call_cfunc_15;
-      default:
-	rb_bug("call_cfunc_func: unsupported length: %d", argc);
-    }
-}
-
 static void
 setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc)
 {
     cfunc->func = func;
     cfunc->argc = argc;
-    cfunc->invoker = call_cfunc_invoker_func(argc);
+    if (argc < -2 || argc > 15)
+	rb_bug("setup_method_cfunc_struct: unsupported length: %d", argc);
 }
 
 rb_method_entry_t *
-- 
EW

