From 40ef5c4f149d359b3c6aeff0763f9a973bf41ddb Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 20 Jun 2017 13:02:39 +0900 Subject: [PATCH 1/2] PERF: Reduce memory allocation by `Emoji.unicode_replacements`. Calling `Emoji.tonable_emojis` fetches from the cache but creates a giant array each time it is called. ``` MemoryProfiler.report { Emoji.unicode_replacements }.pretty_print ``` Before: ``` Total allocated: 16560770 bytes (246364 objects) Total retained: 784793 bytes (10624 objects) allocated memory by gem ----------------------------------- 10224581 activesupport-4.2.8 3504241 hiredis-0.6.1 1449466 discourse/app 679314 2.4.1/lib 419592 discourse/lib 226720 redis-3.3.3 56680 activerecord-4.2.8 176 thread_safe-0.3.6 ``` After: ``` Total allocated: 2127703 bytes (30556 objects) Total retained: 798099 bytes (10735 objects) allocated memory by gem ----------------------------------- 1270762 discourse/app 690149 2.4.1/lib 106977 bootsnap-1.0.0 48764 activesupport-4.2.8 5266 logster-1.2.7 2473 hiredis-0.6.1 2056 redis-3.3.3 840 discourse/lib 240 activerecord-4.2.8 176 thread_safe-0.3.6 ``` --- app/models/emoji.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/emoji.rb b/app/models/emoji.rb index b450597a4f6..b634a8f3535 100644 --- a/app/models/emoji.rb +++ b/app/models/emoji.rb @@ -118,6 +118,7 @@ class Emoji return @unicode_replacements if @unicode_replacements @unicode_replacements = {} + is_tonable_emojis = Emoji.tonable_emojis db['emojis'].each do |e| next if e['name'] == 'tm' @@ -126,7 +127,7 @@ class Emoji next unless code @unicode_replacements[code] = e['name'] - if Emoji.tonable_emojis.include?(e['name']) + if is_tonable_emojis.include?(e['name']) FITZPATRICK_SCALE.each_with_index do |scale, index| toned_code = (code.codepoints.insert(1, scale.to_i(16))).pack("U*") @unicode_replacements[toned_code] = "#{e['name']}:t#{index+2}" @@ -156,6 +157,7 @@ class Emoji def self.lookup_unicode(name) @reverse_map ||= begin map = {} + is_tonable_emojis = Emoji.tonable_emojis db['emojis'].each do |e| next if e['name'] == 'tm' @@ -164,7 +166,7 @@ class Emoji next unless code map[e['name']] = code - if Emoji.tonable_emojis.include?(e['name']) + if is_tonable_emojis.include?(e['name']) FITZPATRICK_SCALE.each_with_index do |scale, index| toned_code = (code.codepoints.insert(1, scale.to_i(16))).pack("U*") map["#{e['name']}:t#{index+2}"] = toned_code From fa35137d3abd277f5e5ea9c7c9533dcbb65ed4a0 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 20 Jun 2017 13:18:24 +0900 Subject: [PATCH 2/2] PERF: Reduce allocations in `Emoji.unicode_replacements`. ``` MemoryProfiler.report { Emoji.unicode_replacements }.pretty_print ``` Before: ``` Total allocated: 2032131 bytes (30219 objects) Total retained: 798059 bytes (10734 objects) allocated memory by gem ----------------------------------- 1270762 discourse/app 690149 2.4.1/lib 48764 activesupport-4.2.8 11405 bootsnap-1.0.0 5266 logster-1.2.7 2473 hiredis-0.6.1 2056 redis-3.3.3 840 discourse/lib 240 activerecord-4.2.8 176 thread_safe-0.3.6 ``` After: ``` Total allocated: 1715713 bytes (22449 objects) Total retained: 797985 bytes (10737 objects) allocated memory by gem ----------------------------------- 957250 discourse/app 690028 2.4.1/lib 48764 activesupport-4.2.8 11445 bootsnap-1.0.0 2745 hiredis-0.6.1 2624 redis-3.3.3 1473 logster-1.2.7 928 discourse/lib 280 activerecord-4.2.8 176 thread_safe-0.3.6 ``` --- app/models/emoji.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/models/emoji.rb b/app/models/emoji.rb index b634a8f3535..0e81f2aa0fc 100644 --- a/app/models/emoji.rb +++ b/app/models/emoji.rb @@ -108,10 +108,9 @@ class Emoji end def self.replacement_code(code) - hexes = code.split('-').map(&:hex) - + hexes = code.split('-'.freeze).map!(&:hex) # Don't replace digits, letters and some symbols - return hexes.pack("U" * hexes.size) if hexes[0] > 255 + hexes.pack("U*".freeze) if hexes[0] > 255 end def self.unicode_replacements @@ -119,18 +118,20 @@ class Emoji @unicode_replacements = {} is_tonable_emojis = Emoji.tonable_emojis + fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) } db['emojis'].each do |e| - next if e['name'] == 'tm' + name = e['name'] + next if name == 'tm'.freeze code = replacement_code(e['code']) next unless code - @unicode_replacements[code] = e['name'] - if is_tonable_emojis.include?(e['name']) - FITZPATRICK_SCALE.each_with_index do |scale, index| - toned_code = (code.codepoints.insert(1, scale.to_i(16))).pack("U*") - @unicode_replacements[toned_code] = "#{e['name']}:t#{index+2}" + @unicode_replacements[code] = name + if is_tonable_emojis.include?(name) + fitzpatrick_scales.each_with_index do |scale, index| + toned_code = code.codepoints.insert(1, scale).pack("U*".freeze) + @unicode_replacements[toned_code] = "#{name}:t#{index+2}" end end end