Ruby комбинирането на два масива води до изчерпване на паметта на процеса

Комбинирането на два масива от хешове, всеки от които има по 100 хиляди елемента, води до изчерпване на паметта на процес, изпълняван на 2GB VM. Не мога да разбера как/защо.

Да кажем, че имам хеш като този и го попълвам с 50 двойки ключ/стойност.

h = {}
1..50.times{ h[SecureRandom.hex] = SecureRandom.hex}

Поставям 100k hs в два масива така:

a = []
a1 = []
1..100_000.times{ a << h }
1..100_000.times{ a1 << h }

Когато се опитам да добавя a1 в a, IRB изчерпва паметта:

2.1.1 :008 > a << a1
NoMemoryError: failed to allocate memory

Наистина ли двата масива са твърде големи, за да се комбинират в паметта? Какъв е предпочитаният начин да се постигне това?

Изпълнявам ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux] и виртуалната машина няма други процеси, работещи на нея.


person user1032752    schedule 13.08.2014    source източник
comment
100 хиляди артикула? повече от 5 000 000 елемента всеки. 50 * 100 000 = 5 милиона. присъединяването към тях ще създаде масив от 10 милиона елемента.   -  person Marc B    schedule 13.08.2014
comment
Показвам го с помощта на 1,72G реална памет, 4,87G виртуална на Ruby 2.1.2 на OSX. Така че да, напълно възможно е да му свършва ram..   -  person Doon    schedule 13.08.2014
comment
Работи добре за мен. Струва си да се отбележи, че тези списъци имат 100K препратки към един и същ хеш. a << a1 също добавя препратка към a1 в края. Може би искате a += a1.   -  person tadman    schedule 13.08.2014
comment
Не би трябвало. Особено след като a << a1 всъщност не копира нищо друго освен препратка към a1. Подозирам, че това, което наистина се случва, е, че IRB изчерпва паметта, докато се опитва да покаже получения масив.   -  person Ajedi32    schedule 13.08.2014
comment
Може да пропускам нещо, но - хешът се попълва с произволни двойки ключ/стойност петдесет пъти и след това хешът се добавя към всеки масив 100k пъти, което прави два масива със 100k елемента всеки, където всеки елемент е хеш с 50 k/v двойки . Не?   -  person user1032752    schedule 13.08.2014
comment
Прав си. Всеки елемент обаче не е просто a хеш с 50 k/v двойки, това е същият хеш. Така че вероятно използва много по-малко памет, отколкото бихте очаквали.   -  person Ajedi32    schedule 13.08.2014
comment
@tadman a += a1 причинява същото нещо.   -  person user1032752    schedule 13.08.2014
comment
@Ajedi32 и @tadman не биха ли използвали по-малко памет, отколкото повече памет? Освен това, промяната му така, че ` 1..100_000.times{ a ‹‹ h.clone }` причинява същия проблем. Можете ли да ми кажете каква е вашата памет, когато работи? Благодаря!   -  person user1032752    schedule 14.08.2014
comment
@user1032752 Да, препратките наистина използват по-малко памет. Това го казах в коментара си. Опитайте да добавите точка и запетая след последния си израз, за ​​да кажете на IRB да не показва резултата.   -  person Ajedi32    schedule 14.08.2014
comment
@Ajedi32 - Мисля, че си прав, че IRB не може да покаже изхода. Изпълнението на a += a1; nil завършва мигновено без скок в паметта. Искате ли да изпратите отговор за това?   -  person user1032752    schedule 14.08.2014
comment
@user1032752 Ще стане.   -  person Ajedi32    schedule 14.08.2014


Отговори (1)


Проблемът най-вероятно не е, че Ruby изчерпва паметта, докато изпълнява тази операция (особено след като има само едно копие на h хеша), а по-скоро, че IRB изчерпва паметта, докато се опитва да покаже резултата. Опитайте да добавите ; nil след последния ред в IRB; това трябва да реши проблема, тъй като ще попречи на IRB да се опита да покаже хеша на резултата.

Пример:

require 'securerandom'
require 'objspace'

h = {}
1..50.times{ h[SecureRandom.hex] = SecureRandom.hex}
a = []
a1 = []
1..100_000.times{ a << h }
1..100_000.times{ a1 << h }
a << a1; nil # Semicolon and nil are for IRB, not needed with regular Ruby

puts "Total memory usage: #{ObjectSpace.memsize_of_all/1000.0} KB"

Получавам резултат от Total memory usage: 7526.543 KB; не е близо до 2 GB.

person Ajedi32    schedule 13.08.2014
comment
Благодаря отново, попитах това, докато отстранявах този въпрос - връзка - мислите ли, че можете да помогнете изобщо? - person user1032752; 14.08.2014
comment
Възможно е изобразяването на самия списък като низ да причинява грешка в паметта. - person tadman; 14.08.2014
comment
@tadman Възможно е. Честно казано, но не съм сигурен. Всичко, което мога да направя, е да ви насоча към ObjectSpace като потенциален ресурс за отстраняване на грешки. - person Ajedi32; 14.08.2014
comment
Самият масив е доста компактен, това са просто препратки, но разширяването му до низ с inspect може да бъде много болезнено. - person tadman; 14.08.2014