Upgrade to Pro — share decks privately, control downloads, hide ads and more …

640K ought to be enough for anybody, or how to...

Julia Egorova
September 28, 2019

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
Tweet

Other Decks in Programming

Transcript

  1. 640K ought to be enough for anybody, or how to

    use less memory in Ruby Julia Egorova, Back-end developer at JetRockets
  2. What about Ruby? Framework Memory Ruby On Rails ~ 60MB

    Hanami ~ 50MB Sinatra ~ 16MB Roda ~ 15MB
  3. What about Rails? Method Memory Rocket.find(1) ~ 650KB rocket.save ~

    100KB number_to_currency(100) ~ 11KB stylesheet_link_tag("rocket.css") ~ 3Kb
  4. 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
  5. 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)
  6. 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 …
  7. 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
  8. 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
  9. 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
  10. # 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
  11. @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
  12. @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
  13. 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
  14. 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
  15. 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)
  16. 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
  17. # 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
  18. 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
  19. 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
  20. 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
  21. # 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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)
  27. 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
  28. 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)
  29. 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
  30. # 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
  31. # 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. # 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
  38. 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
  39. 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
  40. 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
  41. # 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
  42. # 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. [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
  49. [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
  50. 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
  51. 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
  52. 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
  53. 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
  54. What we learned Do not repeat yourself; Do not create

    objects that you do not need; • •
  55. 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; • • •
  56. 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; • • • •
  57. 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. • • • • •
  58. 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)
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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)
  68. 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 -
  69. In the real life application Report with 128 columns ✕

    1000 rows; 23 columns with number_to_currency. • •
  70. In the real life application Report with 128 columns ✕

    1000 rows; 23 columns with number_to_currency. • • 1.2415% less memory