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.