# Hash key performance

**URL:** <https://www.ruby-forum.com/t/hash-key-performance/116105>\
**Category:** Ruby\
**Created:** [October 2, 2007, 12:25pm UTC](https://www.ruby-forum.com/t/hash-key-performance/116105 "2007-10-02T12:25:03Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Matthias\_WSSS\_chter](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/c67d28/32.png) [@Matthias\_WSSS\_chter](https://www.ruby-forum.com/u/Matthias_WSSS_chter)\
**Post date:** [October 2, 2007, 12:25pm UTC](https://www.ruby-forum.com/t/hash-key-performance/116105/1 "2007-10-02T12:25:03Z")

</div>

I am just wondering why different hash keys result in such a  
significantly different performance.

I have written 11 simple test cases, all using the following template:

num=ARGV[0].to\_i

var=:key

a={:key =\> “test”}

num.times {  
b=a[:key][0]  
}

Beside this test\_symbol.rb variant, I replace :key in the example  
above with false (test\_false.rb), an integer value 1 (test\_int.rb),  
with nil (test\_nil.rb), with “id” (test\_string.rb). In six other  
variants I replace the key with var, and set var to a symbol  
(test\_var\_symbol.rb), false (test\_var\_false.rb), an integer value  
(test\_var\_int.rb), nil (test\_var\_nil.rb), “id” (test\_var\_string.rb),  
and “id”.frozen (test\_var\_frozen.rb).

This is my performance chart using time (dual-core CPU, called with  
num=30\_000\_000). I ran it once (dropped timing values), then three  
consecutive times and added the times (which is the sort key as well):

test\_int.rb 37.029  
test\_var\_int.rb 37.659  
test\_symbol.rb 37.773  
test\_var\_symbol.rb 37.785  
test\_var\_frozen.rb 37.852  
test\_var\_string.rb 40.515  
test\_var\_false.rb 45.468  
test\_false.rb 45.687  
test\_var\_nil.rb 45.894  
test\_nil.rb 46.068  
test\_string.rb 53.032

No surprise is that repeated lookup of a string as a key results in  
the worst performance, and using a Fixnum gives best performance.

More interesting is a bulk of tests that relate to using symbols and  
variables for int, symbol and frozen string, all within timing  
accuracy. A noticable, but still small gap can be seen for a  
nonfrozen string in a variable.

But here it comes: Using false and, even worse, nil (in either  
literal or variable form) is way slower (a good 20%) than using  
integers and symbols, even if they follow the same “immutable  
object” philosophy of fixed object\_ids.

Why is that? What’s so special about false and nil to have such a  
degraded performance as a hash key?

- Matthias

---

<div class="post-metadata">

**Author:** ![Matthias\_WSSS\_chter](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/c67d28/32.png) [@Matthias\_WSSS\_chter](https://www.ruby-forum.com/u/Matthias_WSSS_chter)\
**Post date:** [October 2, 2007, 4:55pm UTC](https://www.ruby-forum.com/t/hash-key-performance/116105/2 "2007-10-02T16:55:41Z")

</div>

Matthias WÃ¤chter wrote:

> But here it comes: Using false and, even worse, nil (in either  
> literal or variable form) is way slower (a good 20%) than using  
> integers and symbols, even if they follow the same “immutable  
> object” philosophy of fixed object\_ids.
> 
> Why is that? What’s so special about false and nil to have such a  
> degraded performance as a hash key?

hash.c:rb\_any\_hash is the answer to your question (funcall(‘hash’) is  
discarded only for Fixnums, Symbols and Strings. Note that String  
numbers would be way better if it’s hash was cached until first  
destructive operation.

lopex

---

<div class="post-metadata">

**Author:** ![Matthias\_WSSS\_chter](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/c67d28/32.png) [@Matthias\_WSSS\_chter](https://www.ruby-forum.com/u/Matthias_WSSS_chter)\
**Post date:** [October 3, 2007, 8:47am UTC](https://www.ruby-forum.com/t/hash-key-performance/116105/3 "2007-10-03T08:47:07Z")

</div>

On 02.10.2007 16:55, Marcin MielÅ¼yÅ„ski wrote:

> hash.c:rb\_any\_hash is the answer to your question (funcall(‘hash’) is  
> discarded only for Fixnums, Symbols and Strings.

So the first two cases of “switch (TYPE(a))” should be enhanced with  
checks for nil, true and false like this:

```
switch (TYPE(a)) {
  case T_FIXNUM:
  case T_SYMBOL:

```

- 

```
case T_NIL:

```

- 

```
case T_FALSE:

```

- 

```
case T_TRUE:

```

return (int)a;  
break;

Objections?

- Matthias

---

<div class="post-metadata">

**Author:** ![Matthias\_WSSS\_chter](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/c67d28/32.png) [@Matthias\_WSSS\_chter](https://www.ruby-forum.com/u/Matthias_WSSS_chter)\
**Post date:** [October 3, 2007, 10:20pm UTC](https://www.ruby-forum.com/t/hash-key-performance/116105/4 "2007-10-03T22:20:19Z")

</div>

On Oct 2, 2007, at 23:46 , Matthias WÃ¤chter wrote:

> > >
> 
> - 
> 
> ```
> case T_NIL:
> 
> ```
> 
> - 
> 
> ```
> case T_FALSE:
> 
> ```
> 
> - 
> 
> ```
> case T_TRUE:
> 
> ```
> 
> return (int)a;  
> break;

Send that and your original timings to ruby-core@ and/or file a bug  
on rubyforge in the ruby project.
