Hoje fiz vários testes de gerência de memória e achei interessante como cada ganho de byte pode ser interessante conforme a quantidade de dados que se tem para manusear. Apesar da memória ser barata, tratando-se de performance e escalabilidade o barato sai caro muitas vezes.
Então olhando para os gargalos do sistema observei um hash muito utilizado em um determinado sistema e então resolvi montar esta comparação.
A brincadeira começa com uma simples array de 1000_000 registros e populamos ela das seguintes formas:
Hash - memory_a.rb
a = [] sum = 0 100000.times do |i| sum += 1 a << {:i => i, :sum => sum} end
Struct - memory_b.rb
a = [] B = Struct.new :i, :sum sum = 0 100000.times do |i| sum += 1 a << B.new(i,sum) end
Array - memory_c.rb
i = [] sums = [] sum = 0 100000.times do |i| sum += 1 i << i sums << sum end
Class com métodos - memory_d.rb
a = [] class B def initialize i, sum @i = i @sum = sum end def i @i end def sum @sum end end sum = 0 100000.times do |i| sum += 1 a << B.new(i,sum) end
Class com accessors - memory_e.rb
a = [] sum = 0 class B attr_accessor :i, :sum def initialize i, sum @i = i @sum = sum end end 100000.times do |i| sum += 1 a << B.new(i,sum) end
Class sem métodos - memory_f.rb
class B instance_methods.each { |m| undef_method m unless m =~ /^__|object_id/ } attr_accessor :i, :sum def initialize i, sum @i = i @sum = sum end end a = [] sum = 0 100000.times do |i| sum += 1 a << B.new(i,sum) end
Observando os resultados com jruby-1.7.3 temos os seguintes tamanhos de memória em bytes:
arquivo | memória |
---|---|
memory_a.rb | 106409984 |
memory_b.rb | 94859264 |
memory_c.rb | 95125504 |
memory_d.rb | 95064064 |
memory_e.rb | 94691328 |
memory_f.rb | 94236672 |
No meu caso o modelo memory_f parece ser o mais adequado em vista do consumo de memória e a implementação também é relativamente simples. O interessante destes objetos em branco é por que de certa forma eles não podem ser clonados, mas para casos específicos podem ajudar muito, neste caso têm uma significância de aproximadamente 12%.
Baixe e teste o code você mesmo!
Assim que sobrar um tempo vou evoluir os modelos para comparar entre tipos de númericos. Hoje estamos usando o java.math.BigDecimal para cálculos precisos mas este é um dos mais lentos e grandes para trabalhar com números.