Прожорливый JSON

September 2, 2016

Часто промежуточные результаты каких-нибудь долгих вычислений, которые нужно сделать один раз, сохраняю в JSON. Например могу пройтись по всем записям в большой таблице, посчитать серию параметров, сохранить в JSON, скачать файл локально и потом считать какие-нибудь агрегированные данные.

Однажды, считая большой отчет, наткнулся на необычайную прожорливость JSON.load на сравнительно небольших данных.

Промоделируем ситуацию (размер занимаемой памяти на вашем компьютере может отличаться, но я проверил на маке и на линуксе). Сгенерируем json-файл:

# => 11 MB
a = {key: [{k: 1, value: "strng"}]*2_000_000}
# => 26 MB
t = JSON.dump(a)
# => 54 MB
File.write("big.json", t)

Теперь его прочитаем:

# => 11 MB
t = File.read("big.json")
# => 57 MB
x = JSON.load(t)
# => 1600 MB

:scream_cat: :scream_cat: :scream_cat: массив, который в руби занимает 15 мегабайт, после восстановления раздувается в 100 раз (при использовании библиотеки oj в 50 раз). Самое противное в этой ситуации, что json на 1 гигабайт сохранить вы сможете, а прочитать уже нет - памяти не хватит. Особенно неприятно об этом узнать после нескольких часов вычислений.

Чтобы обойти эту проблему можно использовать пару Marshal.dump/load, которая работает гораздо быстрее и не занимает лишней памяти. Однако marshal-файл не получится посмотреть глазами, нормально хранить в системе контроля версии или погрепать. Поэтому универсального решения не существует, все зависит от задачи. Просто помните, когда сохраняете большой JSON-файл, а хватит ли вам духа и оперативной памяти, чтобы его прочитать?

comments powered by Disqus