From 9b4d7c51aeba2b1e009bbf42f45df4f0afbddf66 Mon Sep 17 00:00:00 2001 From: Jake Landis Date: Mon, 24 Jul 2017 21:17:40 -0500 Subject: [PATCH] Performance: Use RubyArray.hash for metric's fast lookup key The existing implementation uses the RubyArray as the key for the fast lookup Map. Under the covers the Map implementation is comparing equal operators (many times per event), and JRuby Array equals operator is VERY expensive since it literally walks each value of array for each equality. Based on profiling via YourKit, the JRuby Array equals operator is a very hot method consuming upto 60% of sampled CPU cycles while under high load (and no other CPU dominators). This change is to use the .hash value of the JRuby Array as the key of the fast lookup Map. This implementation still calls .hash for each and every call, which is also expensive, since it also walks the arrays to compute the hash. However, the equality check of the hash value is very fast, and net gain is significant. Upto a 15% increase of throughput. Fixes #7772 Fixes #7798 --- .../lib/logstash/instrument/metric_store.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/logstash-core/lib/logstash/instrument/metric_store.rb b/logstash-core/lib/logstash/instrument/metric_store.rb index 09e803a46..c6bfe8841 100644 --- a/logstash-core/lib/logstash/instrument/metric_store.rb +++ b/logstash-core/lib/logstash/instrument/metric_store.rb @@ -51,12 +51,14 @@ module LogStash module Instrument # BUT. If the value is not present in the `@fast_lookup` the value will be inserted and we assume that we don't # have it in the `@metric_store` for structured search so we add it there too. - value = @fast_lookup.get(namespaces.dup << key) + # array.hash as the key since it is faster then using the array itself, see #7772 + fast_lookup_key = (namespaces.dup << key).hash + value = @fast_lookup.get(fast_lookup_key) if value.nil? value = block_given? ? yield(key) : default_value - @fast_lookup.put(namespaces.dup << key, value) + @fast_lookup.put(fast_lookup_key, value) @structured_lookup_mutex.synchronize do - # If we cannot find the value this mean we need to save it in the store. + # If we cannot find the value this mean we need to save it in the store. fetch_or_store_namespaces(namespaces).fetch_or_store(key, value) end end @@ -163,7 +165,7 @@ module LogStash module Instrument end def has_metric?(*path) - @fast_lookup[path] + @fast_lookup[path.hash] end # Return all the individuals Metric, @@ -185,8 +187,9 @@ module LogStash module Instrument def prune(path) key_paths = key_paths(path).map(&:to_sym) @structured_lookup_mutex.synchronize do - keys_to_delete = @fast_lookup.keys.select {|namespace| (key_paths - namespace[0..-2]).empty? } - keys_to_delete.each {|k| @fast_lookup.delete(k) } + fetch_or_store_namespaces(key_paths).each do |key, v| + @fast_lookup.delete((key_paths.dup << key).hash) + end delete_from_map(@store, key_paths) end end