Skip to content

Instantly share code, notes, and snippets.

@supercaracal
Last active September 25, 2022 13:18
Show Gist options
  • Save supercaracal/e9992394d64bb0f52e9f00a090060f64 to your computer and use it in GitHub Desktop.
Save supercaracal/e9992394d64bb0f52e9f00a090060f64 to your computer and use it in GitHub Desktop.
Memory allocation comparison in Ruby

Array vs StringArray

This use case is under conditions that the array size is huge and the kind of elements are limited.

irb(main):001:0> require 'objspace'
=> true

irb(main):002:0> ObjectSpace.memsize_of(Array.new(16384))
=> 131112

irb(main):003:0> s = '0' * 16384
=> "00000000000000000000000000000000000...

irb(main):004:0> ObjectSpace.memsize_of(String.new(s, encoding: Encoding::BINARY, capacity: 16384))
=> 16425

irb(main):005:0> ObjectSpace.memsize_of(Array.new(256))
=> 2088
StringArray = Struct.new('StringArray', :string, :elements, keyword_init: true) do
  def [](index)
    raise IndexError if index < 0
    return if index >= string.bytesize

    elements[string.getbyte(index)]
  end

  def []=(index, element)
    raise IndexError if index < 0
    return if index >= string.bytesize

    pos = elements.find_index(element) # O(N)
    if pos.nil?
      raise(RangeError, 'full of elements') if elements.size >= 256

      pos = elements.size
      elements << element
    end

    string.setbyte(index, pos)
  end
end

FIXED_ARRAY_SIZE = 16384
BASE_STRING = '0' * FIXED_ARRAY_SIZE

base_elements = %w[a b c d e f g]

arr = if base_elements.size > 256
        Array.new(FIXED_ARRAY_SIZE)
      else
        StringArray.new(
          string: String.new(BASE_STRING, encoding: Encoding::BINARY, capacity: FIXED_ARRAY_SIZE),
          elements: base_elements
        )
      end

FIXED_ARRAY_SIZE.times do { |i| arr[i] = base_elements.sample }

Hash vs Struct

In certain use cases, it would be better to use struct instead of hash for memory efficiency.

irb(main):006:0> ObjectSpace.memsize_of({a:1, b:2, c:3, d:4, e:5})
=> 168

irb(main):007:0> Foo = Struct.new('Foo', :a, :b, :c, :d, :e, keyword_init: true)
=> Struct::Foo(keyword_init: true)

irb(main):008:0> ObjectSpace.memsize_of(Foo.new(a:1, b:2, c:3, d:4, e:5))
=> 80
@supercaracal
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment