From ddfcbf85cb34f99b629f2ad621eb7f8c09d38db9 Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Wed, 20 Jan 2016 01:03:18 +0000
Subject: [PATCH] psych: pre-freeze string keys for hashes

With the following example, this reduces allocations from 346 to 324
strings when calling Psych.load on a 26-entry hash:

-------------------------------8<--------------------------------
require 'psych'
require 'objspace'
before = {}
after = {}
str = [ '---', *(('a'..'z').map { |k| "#{k * 11}: 1" }), '' ].join("\n")
GC.disable
ObjectSpace.count_objects(before)
h = Psych.load(str)
ObjectSpace.count_objects(after)
p(after[:T_STRING] - before[:T_STRING])
-------------------------------8<--------------------------------

Allocating 324 strings for 26 hash keys is still expensive.  More
work will be needed to reduce allocations further...

Tested on x86-64.
---
 ext/psych/lib/psych/visitors/to_ruby.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index c061da2..2f9408c 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -336,6 +336,7 @@ def revive_hash hash, o
         o.children.each_slice(2) { |k,v|
           key = accept(k)
           val = accept(v)
+          key.freeze if key.instance_of?(String)
 
           if key == SHOVEL && k.tag != "tag:yaml.org,2002:str"
             case v
-- 
EW

