Share
Embed
640K ought to be enough for anybody, or how to use less memory in Ruby.
Ought 640K to be enough for anybody? Well, today we can use dozens of RAM, and it does not matter if our applications take up a few gigabytes. Right?
Have you ever been curious about why they use so much? Let's talk about how Ruby memory works, how we can profile memory usage and what to do to use less memory.
Julia Egorova
September 28, 2019
Transcript
182 slides
-
640K ought to be enough for anybody, or how to
use less memory in Ruby Julia Egorova, Back-end developer at JetRockets -
640k outght to be enough for anybody. » « Bill
Gates, 1981 -
I've said some stupid things and some wrong things, but
not that. » « Bill Gates, 1996 -
What about Ruby? Framework Memory Ruby On Rails ~ 60MB
Hanami ~ 50MB Sinatra ~ 16MB Roda ~ 15MB -
What about Rails? Method Memory Rocket.find(1) ~ 650KB rocket.save ~
100KB number_to_currency(100) ~ 11KB stylesheet_link_tag("rocket.css") ~ 3Kb -
Tiny, Small and Large Objects VALUE *ptr → RValue typedef
uintptr_t VALUE; -
GC.stat > GC.stat => { :count=>62, :heap_allocated_pages=>1416, :heap_available_slots=>577147, :heap_live_slots=>567649, :heap_free_slots=>9498,
:total_allocated_pages=>1416, :total_allocated_objects=>2165898, … } -
ObjectSpace > ObjectSpace.count_objects => { :TOTAL=>577147, :FREE=>9441, :T_OBJECT=>43380, :T_CLASS=>10129, :T_MODULE=>1542,
:T_FLOAT=>9, :T_STRING=>272473, … } -
rockets = [ "Saturn V", "N1", "Energia Buran", "Falcon Heavy"
] ObjectSpace.each_object(String) do |object| puts "#{object} – #{ObjectSpace.memsize_of(object)}" end => Saturn V – 40 N1 – 40 Energia Buran – 40 Falcon Heavy – 40 -
memory_profiler require 'memory_profiler' report = MemoryProfiler.report do Rocket.find(1) end report.pretty_print
=> Total allocated: 692569 bytes (8457 objects) Total retained: 46495 bytes (640 objects) -
Total allocated: 692569 bytes (8457 objects) Total retained: 46495 bytes
(640 objects) allocated memory by class ----------------------------------- 632937 String 37656 Array 8424 File 6624 Hash … allocated objects by class ----------------------------------- 7468 String 899 Array 23 Hash 16 Symbol … -
get_process_mem require 'get_process_mem' memory = GetProcessMem.new memory.bytes #=> 138735616 memory.kb
#=> 135488.0 memory.mb #=> 132.37891 memory.gb #=> 0.1293449 -
derailed_benchmarks $ bundle exec derailed bundle:mem TOP: 61.5313 MiB rails/all:
25.332 MiB rails: 11.7891 MiB slim: 1.6992 MiB slim/engine: 0.6016 MiB slim/parser: 0.3086 MiB dry-struct: 1.0508 MiB dry/struct: 1.0508 MiB -
derailed_benchmarks $ bundle exec derailed bundle:objects Total allocated: 36578388 bytes
(337639 objects) Total retained: 4555830 bytes (37728 objects) allocated memory by gem ----------------------------------- 28982982 activesupport-5.1.6.1 1030516 forwardable 980919 graphql-1.7.7 -
Total allocated: 11044 bytes (110 objects) Total retained: 0 bytes
(0 objects) allocated memory by class ----------------------------------- 5864 Hash 1792 String 1760 Array 1120 MatchData 180 BigDecimal allocated objects by class ----------------------------------- 36 Array 32 String 29 Hash 4 MatchData 2 BigDecimal -
# active_support/number_helper/number_converter.rb def options @options ||= format_options.merge(opts) end def format_options
default_format_options.merge!(i18n_format_options) end def default_format_options options = DEFAULTS[:format].dup options.merge!(DEFAULTS[namespace][:format]) if namespace options end -
@options ||= format_options.merge(opts) # active_support/number_helper/number_converter.rb def options end def format_options
default_format_options.merge!(i18n_format_options) end def default_format_options options = DEFAULTS[:format].dup options.merge!(DEFAULTS[namespace][:format]) if namespace options end -
@options ||= format_options.merge(opts) # active_support/number_helper/number_converter.rb def options end def format_options
default_format_options.merge!(i18n_format_options) end def default_format_options options = DEFAULTS[:format].dup options.merge!(DEFAULTS[namespace][:format]) if namespace options end Class Objects Memory Hash 1 192 bytes -
default_format_options.merge!(i18n_format_options) # active_support/number_helper/number_converter.rb def options @options ||= format_options.merge(opts) end def
format_options end def default_format_options options = DEFAULTS[:format].dup options.merge!(DEFAULTS[namespace][:format]) if namespace options end Class Objects Memory Hash 1 192 bytes -
options = DEFAULTS[:format].dup # active_support/number_helper/number_converter.rb def options @options ||= format_options.merge(opts)
end def format_options default_format_options.merge!(i18n_format_options) end def default_format_options options.merge!(DEFAULTS[namespace][:format]) if namespace options end Class Objects Memory Hash 1 192 bytes -
options = DEFAULTS[:format].dup # active_support/number_helper/number_converter.rb def options @options ||= format_options.merge(opts)
end def format_options default_format_options.merge!(i18n_format_options) end def default_format_options options.merge!(DEFAULTS[namespace][:format]) if namespace options end Class Objects Memory Hash 2 (+1) 384 bytes (+192) -
options.merge!(DEFAULTS[namespace][:format]) if namespace # active_support/number_helper/number_converter.rb def options @options ||= format_options.merge(opts)
end def format_options default_format_options.merge!(i18n_format_options) end def default_format_options options = DEFAULTS[:format].dup options end Class Objects Memory Hash 2 384 bytes -
# active_support/number_helper/number_converter.rb def i18n_format_options locale = opts[:locale] options = I18n.translate(
:'number.format', locale: locale, default: {}).dup if namespace options.merge!(I18n.translate( :"number.#{namespace}.format", locale: locale, default: {})) end options end Class Objects Memory Hash 2 384 bytes -
options = I18n.translate( :'number.format', locale: locale, default: {}).dup # active_support/number_helper/number_converter.rb
def i18n_format_options locale = opts[:locale] if namespace options.merge!(I18n.translate( :"number.#{namespace}.format", locale: locale, default: {})) end options end Class Objects Memory Hash 2 384 bytes -
options = I18n.translate( :'number.format', locale: locale, default: {}).dup # active_support/number_helper/number_converter.rb
def i18n_format_options locale = opts[:locale] if namespace options.merge!(I18n.translate( :"number.#{namespace}.format", locale: locale, default: {})) end options end Class Objects Memory Hash 7 (+5) 1344 bytes (+960) Array 7 280 bytes -
if namespace options.merge!(I18n.translate( :"number.#{namespace}.format", locale: locale, default: {})) end #
active_support/number_helper/number_converter.rb def i18n_format_options locale = opts[:locale] options = I18n.translate( :'number.format', locale: locale, default: {}).dup options end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes -
# active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip format = options[:format]
if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes -
number = self.number.to_s.strip # active_support/number_helper/number_to_currency_converter.rb def convert format = options[:format]
if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes -
number = self.number.to_s.strip # active_support/number_helper/number_to_currency_converter.rb def convert format = options[:format]
if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 2 80 bytes -
number = absolute_value(number) # active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip
format = options[:format] if number.to_f.negative? format = options[:negative_format] end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 2 80 bytes -
number = absolute_value(number) def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/,
"") end # active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip format = options[:format] if number.to_f.negative? format = options[:negative_format] end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 2 80 bytes -
number = absolute_value(number) def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/,
"") end # active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip format = options[:format] if number.to_f.negative? format = options[:negative_format] end rounded_number = NumberToRoundedConverter.convert(number, options) format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 4 (+2) 160 bytes (+80) -
format.gsub("%n", rounded_number).gsub("%u", options[:unit]) # active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip
format = options[:format] if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end rounded_number = NumberToRoundedConverter.convert(number, options) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 4 160 bytes -
format.gsub("%n", rounded_number).gsub("%u", options[:unit]) # active_support/number_helper/number_to_currency_converter.rb def convert number = self.number.to_s.strip
format = options[:format] if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end rounded_number = NumberToRoundedConverter.convert(number, options) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 10 (+6) 400 bytes (+240) -
rounded_number = NumberToRoundedConverter.convert(number, options) # active_support/number_helper/number_to_currency_converter.rb def convert number =
self.number.to_s.strip format = options[:format] if number.to_f.negative? format = options[:negative_format] number = absolute_value(number) end format.gsub("%n", rounded_number).gsub("%u", options[:unit]) end def absolute_value(number) number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "") end Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 10 400 bytes -
# active_support/number_helper/number_to_rounded_converter.rb def convert helper = RoundingHelper.new(options) rounded_number = helper.round(number)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 7 1344 bytes Array 7 280 bytes String 10 400 bytes -
# active_support/number_helper/number_to_rounded_converter.rb def convert helper = RoundingHelper.new(options) rounded_number = helper.round(number)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 14 (+7) 2688 bytes (+1344) Array 14 (+7) 560 bytes (+280) String 10 400 bytes -
helper = RoundingHelper.new(options) # active_support/number_helper/number_to_rounded_converter.rb def convert rounded_number = helper.round(number)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes -
helper = RoundingHelper.new(options) # active_support/number_helper/number_to_rounded_converter.rb def convert rounded_number = helper.round(number)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes -
rounded_number = helper.round(number) # active_support/number_helper/number_to_rounded_converter.rb def convert helper = RoundingHelper.new(options)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes -
rounded_number = helper.round(number) # active_support/number_helper/number_to_rounded_converter.rb def convert helper = RoundingHelper.new(options)
if precision = options[:precision] if options[:significant] && precision > 0 digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end … Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
if precision = options[:precision] if options[:significant] && precision > 0
digits = helper.digit_count(rounded_number) precision -= digits precision = 0 if precision < 0 # don't let it be negative end # active_support/number_helper/number_to_rounded_converter.rb def convert helper = RoundingHelper.new(options) rounded_number = helper.round(number) … Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
# active_support/number_helper/number_to_rounded_converter.rb … formatted_string = if BigDecimal === rounded_number &&
rounded_number.finite? s = rounded_number.to_s("F") s << "0" * precision a, b = s.split(".", 2) a << "." a << b[0, precision] else "%00.#{precision}f" % rounded_number end else formatted_string = rounded_number end number = NumberToDelimitedConverter.convert(formatted_string, options) format_number(number) end Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
s = rounded_number.to_s("F") s << "0" * precision a, b
= s.split(".", 2) a << "." a << b[0, precision] # active_support/number_helper/number_to_rounded_converter.rb … formatted_string = if BigDecimal === rounded_number && rounded_number.finite? else "%00.#{precision}f" % rounded_number end else formatted_string = rounded_number end number = NumberToDelimitedConverter.convert(formatted_string, options) format_number(number) end Class Objects Memory Hash 14 2688 bytes Array 14 560 bytes String 10 400 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
s = rounded_number.to_s("F") s << "0" * precision a, b
= s.split(".", 2) a << "." a << b[0, precision] # active_support/number_helper/number_to_rounded_converter.rb … formatted_string = if BigDecimal === rounded_number && rounded_number.finite? else "%00.#{precision}f" % rounded_number end else formatted_string = rounded_number end number = NumberToDelimitedConverter.convert(formatted_string, options) format_number(number) end Class Objects Memory Hash 14 2688 bytes Array 15 (+1) 600 bytes (+40) String 15 (+5) 600 bytes (+200) RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
number = NumberToDelimitedConverter.convert(formatted_string, options) # active_support/number_helper/number_to_rounded_converter.rb … formatted_string = if
BigDecimal === rounded_number && rounded_number.finite? s = rounded_number.to_s("F") s << "0" * precision a, b = s.split(".", 2) a << "." a << b[0, precision] else "%00.#{precision}f" % rounded_number end else formatted_string = rounded_number end format_number(number) end Class Objects Memory Hash 14 2688 bytes Array 15 600 bytes String 15 600 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
# active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end private def parts left,
right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 14 2688 bytes Array 15 600 bytes String 15 600 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
# active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end private def parts left,
right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 (+7) 4032 bytes (+1344) Array 22 (+7) 880 bytes (+280) String 15 600 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
parts.join(options[:separator]) # active_support/number_helper/number_to_delimited_converter.rb def convert end private def parts left,
right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 22 880 bytes String 15 600 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
left, right = number.to_s.split(".") # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end
private def parts left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 22 880 bytes String 15 600 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
left, right = number.to_s.split(".") # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end
private def parts left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 23 (+1) 920 bytes (+40) String 17 (+2) 680 bytes (+80) RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator])
end private def parts left, right = number.to_s.split(".") [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 23 920 bytes String 22 880 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes -
left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator])
end private def parts left, right = number.to_s.split(".") [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 23 920 bytes String 30 (+8) 1200 bytes (+320) RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes -
[left, right].compact # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end private def
parts left, right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end end Class Objects Memory Hash 21 4032 bytes Array 23 920 bytes String 30 1200 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes -
[left, right].compact # active_support/number_helper/number_to_delimited_converter.rb def convert parts.join(options[:separator]) end private def
parts left, right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end end Class Objects Memory Hash 21 4032 bytes Array 25 (+2) 1000 bytes (+80) String 30 1200 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes -
parts.join(options[:separator]) # active_support/number_helper/number_to_delimited_converter.rb def convert end private def parts left,
right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 25 1000 bytes String 30 1200 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes -
parts.join(options[:separator]) # active_support/number_helper/number_to_delimited_converter.rb def convert end private def parts left,
right = number.to_s.split(".") left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end [left, right].compact end Class Objects Memory Hash 21 4032 bytes Array 25 1000 bytes String 31 (+1) 1240 bytes (+40) RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes -
Class Objects Memory Hash 21 4032 bytes Array 25 1000
bytes String 31 1240 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes Total 82 7048 bytes -
Class Objects Memory Hash 21 4032 bytes Array 25 1000
bytes String 31 1240 bytes RoundingHelper 1 40 bytes BigDecimal 2 176 bytes MatchData 2 560 bytes Total 82 7048 bytes -
What we learned Do not repeat yourself; Do not create
objects that you do not need; • • -
What we learned Do not repeat yourself; Do not create
objects that you do not need; Use bang methods to modify an object instead of creating a new one; • • • -
What we learned Do not repeat yourself; Do not create
objects that you do not need; Use bang methods to modify an object instead of creating a new one; Use methods like insert to modify a string instead of creating a new one; • • • • -
What we learned Do not repeat yourself; Do not create
objects that you do not need; Use bang methods to modify an object instead of creating a new one; Use methods like insert to modify a string instead of creating a new one; Think of an algorithm that does not use much memory. • • • • • -
97532.5734 → $ 97,532.57 def number_to_currency(number, options) string = number_to_string(number,
options) add_delimiters(string, number, options) -
97532.5734 → $ 97,532.57 def number_to_currency(number, options) string = number_to_string(number,
options) add_delimiters(string, number, options) add_unit(string, number, options) -
97532.5734 → $ 97,532.57 def number_to_currency(number, options) string = number_to_string(number,
options) add_delimiters(string, number, options) add_unit(string, number, options) string end -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") fractional = string.size - dot_index - 1 -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") fractional = string.size - dot_index - 1 -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") fractional = string.size - dot_index - 1 zeros_to_add = options[:precision] - fractional -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") fractional = string.size - dot_index - 1 zeros_to_add = options[:precision] - fractional -
97532.5734 → 97532.57 def number_to_string(number, options) string = number.round( options[:precision]).to_s
dot_index = string.rindex(".") fractional = string.size - dot_index - 1 zeros_to_add = options[:precision] - fractional zeros_to_add.times do string.insert(-1, "0") end end -
97532.57 → 97,532.57 def add_delimiters(string, number, options) offset = number
> 0 ? 0 : 1 index = -5 - options[:precision] -
97532.57 → 97,532.57 def add_delimiters(string, number, options) offset = number
> 0 ? 0 : 1 index = -5 - options[:precision] -
97532.57 → 97,532.57 def add_delimiters(string, number, options) offset = number
> 0 ? 0 : 1 index = -5 - options[:precision] while offset - index <= string.size do string.insert(index, ",") index -= 4 end end -
97,532.57 → $ 97,532.57 def add_unit(string, number, options) index =
number > 0 ? 0 : 1 -
97,532.57 → $ 97,532.57 def add_unit(string, number, options) index =
number > 0 ? 0 : 1 -
97,532.57 → $ 97,532.57 def add_unit(string, number, options) index =
number > 0 ? 0 : 1 string.insert(index, " ") string.insert(index, options[:unit]) end -
Memory report = MemoryProfiler.new do number_to_currency(97532.5734) end report.pretty_print => Total
allocated: 40 bytes (1 objects) Total retained: 0 bytes (0 objects) -
Comparison ActiveSupport FasterSupport Memory 11 044 bytes 40 bytes Objects
110 objects 1 object -
41.74x slower Performance Checking for BigDecimal Warming up -------------------------------------- ActiveSupport
874.000 i/100ms FasterSupport 30.613k i/100ms Calculating ------------------------------------- ActiveSupport 9.263k (± 5.2%) i/s - 46.322k in 5.015584s FasterSupport 386.647k (± 2.9%) i/s - 1.959M in 5.071773s Comparison: FasterSupport: 386647.3 i/s ActiveSupport: 9262.8 i/s - -
In the real life application Report with 128 columns ✕
1000 rows; 23 columns with number_to_currency. • • -
In the real life application Report with 128 columns ✕
1000 rows; 23 columns with number_to_currency. • • 1.2415% less memory