Issue #10232 has been updated by Eric Wong.


 ko1 / atdot.net wrote:
 > (1) can be incompatibility issue so that I don't propose this fix strongly.
 > Interpreter core doesn't depends on this spec, but C-exts can be affected.
 
 I think the only thing I've seen C-exts depend on is Qfalse == 0
 (which this preserves)
 
 > Strange thing is: vm1_const
 
 I suspect this is CPU-specific behaviors.  I cannot see the difference
 on my AMD FX-8320 (on vm1_const, haven't tested others)

----------------------------------------
Bug #10232: Trivial change of IMMEDIATE VALUE bits layout
https://bugs.ruby-lang.org/issues/10232#change-48861

* Author: Koichi Sasada
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* Category: core
* Target version: current: 2.2.0
* ruby -v: 2.2
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN
----------------------------------------

The following patch improves performance a bit.

```
!USE_FLONUM
-------------------------
...xxxx xxx1 Fixnum
...0000 1110 Symbol
...0000 0000 Qfalse
...0000 0010 Qtrue
...0000 0100 Qnil
...0000 0110 Qundef

USE_FLONUM
-------------------------
...xxxx xxx1 Fixnum
...xxxx xx10 Flonum
...0000 1100 Symbol
...0000 0000 Qfalse  0x00 =  0
...0000 1000 Qnil    0x08 =  8
...0001 0100 Qtrue   0x14 = 20
...0011 0100 Qundef  0x34 = 52

#=>

!USE_FLONUM
-------------------------
xxxx xxxx xxx1 Fixnum  0x01
xxxx 0011 0010 Symbol  0x32 = 50
0000 0000 0000 Qfalse  0x00
0000 0000 0010 Qnil    0x02
0000 0001 0010 Qtrue   0x12 = 18
0000 0010 0010 Qundef  0x22 = 32

USE_FLONUM
-------------------------
xxxx xxxx xxx1 Fixnum  0x01
xxxx xxxx xx10 Flonum  0x02
xxxx 0011 0100 Symbol  0x34 = 52
0000 0000 0000 Qfalse  0x00 =  0
0000 0000 0100 Qnil    0x04 =  4
0000 0001 0100 Qtrue   0x14 = 20
0000 0010 0100 Qundef  0x24 = 36
```

```patch
Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 47531)
+++ include/ruby/ruby.h	(working copy)
@@ -395,6 +395,27 @@ USE_FLONUM
 ...0000 1000 Qnil    0x08 =  8
 ...0001 0100 Qtrue   0x14 = 20
 ...0011 0100 Qundef  0x34 = 52
+
+#=>
+
+!USE_FLONUM
+-------------------------
+xxxx xxxx xxx1 Fixnum  0x01
+xxxx 0011 0010 Symbol  0x32 = 50
+0000 0000 0000 Qfalse  0x00
+0000 0000 0010 Qnil    0x02
+0000 0001 0010 Qtrue   0x12 = 18
+0000 0010 0010 Qundef  0x22 = 32
+
+USE_FLONUM
+-------------------------
+xxxx xxxx xxx1 Fixnum  0x01
+xxxx xxxx xx10 Flonum  0x02
+xxxx 0011 0100 Symbol  0x34 = 52
+0000 0000 0000 Qfalse  0x00 =  0
+0000 0000 0100 Qnil    0x04 =  4
+0000 0001 0100 Qtrue   0x14 = 20
+0000 0010 0100 Qundef  0x24 = 36
  */
 
 /* special constants - i.e. non-zero and non-fixnum constants */
@@ -402,26 +423,26 @@ enum ruby_special_consts {
 #if USE_FLONUM
     RUBY_Qfalse = 0x00,
     RUBY_Qtrue  = 0x14,
-    RUBY_Qnil   = 0x08,
-    RUBY_Qundef = 0x34,
+    RUBY_Qnil   = 0x04,
+    RUBY_Qundef = 0x24,
 
     RUBY_IMMEDIATE_MASK = 0x07,
     RUBY_FIXNUM_FLAG    = 0x01,
     RUBY_FLONUM_MASK    = 0x03,
     RUBY_FLONUM_FLAG    = 0x02,
-    RUBY_SYMBOL_FLAG    = 0x0c,
+    RUBY_SYMBOL_FLAG    = 0x34,
     RUBY_SPECIAL_SHIFT  = 8
 #else
-    RUBY_Qfalse = 0,
-    RUBY_Qtrue  = 2,
-    RUBY_Qnil   = 4,
-    RUBY_Qundef = 6,
+    RUBY_Qfalse = 0x00,
+    RUBY_Qtrue  = 0x12,
+    RUBY_Qnil   = 0x02,
+    RUBY_Qundef = 0x22,
 
     RUBY_IMMEDIATE_MASK = 0x03,
     RUBY_FIXNUM_FLAG    = 0x01,
     RUBY_FLONUM_MASK    = 0x00,	/* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */
     RUBY_FLONUM_FLAG    = 0x02,
-    RUBY_SYMBOL_FLAG    = 0x0e,
+    RUBY_SYMBOL_FLAG    = 0x32,
     RUBY_SPECIAL_SHIFT  = 8
 #endif
 };
@@ -1106,7 +1127,7 @@ struct RStruct {
 #define FL_USER18    (((VALUE)1)<<(FL_USHIFT+18))
 #define FL_USER19    (((VALUE)1)<<(FL_USHIFT+19))
 
-#define SPECIAL_CONST_P(x) (IMMEDIATE_P(x) || !RTEST(x))
+#define SPECIAL_CONST_P(x) (IMMEDIATE_P(x) || !(x))
 
 #define FL_ABLE(x) (!SPECIAL_CONST_P(x) && BUILTIN_TYPE(x) != T_NODE)
 #define FL_TEST_RAW(x,f) (RBASIC(x)->flags&(f))
@@ -1583,11 +1604,9 @@ rb_class_of(VALUE obj)
 	if (FLONUM_P(obj)) return rb_cFloat;
 	if (obj == Qtrue)  return rb_cTrueClass;
 	if (STATIC_SYM_P(obj)) return rb_cSymbol;
-    }
-    else if (!RTEST(obj)) {
 	if (obj == Qnil)   return rb_cNilClass;
-	if (obj == Qfalse) return rb_cFalseClass;
     }
+    else if (obj == Qfalse) return rb_cFalseClass;
     return RBASIC(obj)->klass;
 }
 
@@ -1600,11 +1619,9 @@ rb_type(VALUE obj)
         if (obj == Qtrue)  return T_TRUE;
 	if (STATIC_SYM_P(obj)) return T_SYMBOL;
 	if (obj == Qundef) return T_UNDEF;
-    }
-    else if (!RTEST(obj)) {
 	if (obj == Qnil)   return T_NIL;
-	if (obj == Qfalse) return T_FALSE;
     }
+    else if (obj == Qfalse) return T_FALSE;
     return BUILTIN_TYPE(obj);
 }
 
```

The change is:

(1) IMMEDIATE_P(Qnil) => TRUE
(2) SPECIAL_CONST_P(x) becomes a bit simple -> (IMMEDIATE_P(x) || !(x))

(1) can be incompatibility issue so that I don't propose this fix strongly.
Interpreter core doesn't depends on this spec, but C-exts can be affected.


Benchmark result is: http://www.atdot.net/sp/raw/stirbn (gitruby is modified version)

Strange thing is: vm1_const

```
vm1_const

Const = 1

i = 0
while i<30_000_000 # while loop 1
  i += 1
  j = Const
  k = Const
end

trunk	1.057002067565918
trunk	1.07387113571167
trunk	1.3638195991516113
trunk	1.076874017715454
trunk	1.0717082023620605
gitruby	0.7370171546936035
gitruby	0.7366814613342285
gitruby	0.7377498149871826
gitruby	0.7381434440612793
gitruby	0.7375714778900146
```

vm1_const only repeats `getinlinecache' instruction. I'm not sure why it has impact.

Change this program to repeat accessing constant more:

```
Const = 1

i = 0
while i<30_000_000 # while loop 1
  i += 1
  j = Const
  k = Const
    k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const
  k = Const

end

trunk   2.2485034465789795
trunk   2.2310359477996826
trunk   2.2247872352600098
trunk   2.2434682846069336
trunk   2.225156307220459
gitruby 2.2048048973083496
gitruby 2.2114696502685547
gitruby 2.2110848426818848
gitruby 2.208353042602539
gitruby 2.2142462730407715
```

It can be accidentally.

Other variations:

```
vm1_const

Const = 1

i = 0
while i<30_000_000 # while loop 1
  i += 1
  k = Const
end

trunk   0.6202449798583984
trunk   0.8297767639160156
trunk   0.6203830242156982
trunk   0.6206128597259521
trunk   0.6202561855316162
gitruby 0.6209316253662109
gitruby 0.6197938919067383
gitruby 0.6191442012786865
gitruby 0.6194281578063965
gitruby 0.6211118698120117
```

```
vm1_const

Const = 1

i = 0
while i<30_000_000 # while loop 1
  i += 1
  j = Const
  k = Const
  l = Const
end

trunk   1.195659875869751
trunk   1.1864018440246582
trunk   1.1857333183288574
trunk   1.17852783203125
trunk   1.177058458328247
gitruby 1.1888437271118164
gitruby 1.1901893615722656
gitruby 1.1873669624328613
gitruby 1.187666893005371
gitruby 1.182875156402588
```

It seems that such strange behaviour only on two Const accessing.





-- 
https://bugs.ruby-lang.org/