Я тоже ошибся в своей попытке локализовать проблемы. Мне почему-то показалось, что эти вектора статической длинны, но судя по всему это что-то аналогичное sso.
Как только у меня появится время займусь оптимизацией С++-варианта.
У меня два замечания. Оно работает неправильно(как минимум оно не читает более чем 4 телефона). Зачем вы сравниваете: User time (seconds): 1.15
User time (seconds): 0.48
Какой в этом смысл?
Так JSON и есть бинарный формат — все разделители байты, остальное в ut8, единственная проблема — нет отдельных делимиторов верхнего уровня, и нет блочности, поэтому распараллелить расчет можно только после первичного поточного парсинга.
Он не бинарный формат в том смысле в котором понимают бинарным форматы. Формально да, все форматы являются бинарными.
Основных отличий много. В бинарном формат строгий/однозначный, а значит в данных ненужно хранить структуру. У вас же оно хранится. Бинарный формат компактней. Ненужно ничего никуда преобразовывать(строки в числа, числа в строки и прочее). Подобные форматы заранее спроектированы таким образом, что-бы хранить данные в наиболее удобной для обработки вида. Про блочность вы уже сказали. Продолжать можно долго.
СУБД и рядом не стояли по производительности на сопоставимом железе, поэтому для бигдаты и не используют реляционку, банально не тянет.
Нужно сравнивать. К тому же БД не обязательно должна быть реляционной. Аналогично, сомневаюсь, что кто-то в бигдате использует json. Для хранения отдельных документов? Возможно. Но то отдельные документы. К тому по эти документам строятся внешние индексы/иерархия.
То есть, в сухом остатке, можно считать, что мы догнали C++?
Нет, почти в 2раза медленнее.
Материал тянет на статью, может кто напишет? :)
Мой код откровенно позорный. Так же я не заниматься оптимизациями. Если вам нужно быстро читать/писать/считать — вы явно выбрали не тот формат хранения и алгоритм. Используйте базу данных для таких задач.
Я примерно предполагаю как ускорить С++-версию ещё раза в 2. Но если использовать бинарный, строгий формат данных — можно получить результат в разы быстрее текущих даже для самой базовой, не оптимизированной версии. Мы просто тратим силы впустую.
mmap(NULL, 99000002, PROT_READ, MAP_SHARED, 3, 0) = 0x7fd176971000
Попробуйте изменить флажки на «как у меня». Здесь mmap нужен не столько для скорости, а сколько для уменьшения потребления памяти. PROT_READ + MAP_POPULATE он сразу мапит пейджкеш в юзерспейс. Без каких-либо аллокация и копирования. В данном случае файл не используется весь, а читается последовательно. Эффект с zero-copy и отсутствия раздувания памяти будет заметен мало.
$ time luajit main.lua
done 1.241739 sec.
[{"debt":910000000,"companies":{"Шестерочка":true,"Рога и копыта":true,"Первая коллекторская":true},"phones":{"788":true,"456":true,"789":true,"2128506":true,"123":true,"234":true}},{"debt":433200000,"companies":{"Святой престол":true,"Казачий спас":true},"phones":{"234567":true,"666":true,"345678":true}}]
Как только у меня появится время займусь оптимизацией С++-варианта.
[
{"company":"Рога и копыта", "debt": 800, "phones": [123, 234, 456]},
{"company":"Первая коллекторская", "debt": 1200, "phones": [2128506, 456, 789, 123, 412, 12412]},
{"company":"Святой престол", "debt": "666", "phones": 666},
{"company": "Казачий спас", "debt": 1500, "phones": [234567, "345678"], "phone": 666},
{"company": {"name": "Шестерочка"}, "debt": 2550, "phones": 788, "phone": 789},
]
2.json:
PROCESSED: 5 objects in 169.14µs, 0 errors found
-------------------------------
#0: debt: 4550
companies: {"Первая коллекторская", "Шестерочка", "Рога и копыта"}
phones: {"234", "12412", "123", "2128506", "788", "412", "789", "456"}
-------------------------------
#1: debt: 2166
companies: {"Святой престол", "Казачий спас"}
phones: {"234567", "345678", "666"}
Запустил и уже подумал, что ошибся. Но нет — не ошибся. Оно попросту выдаёт каждый раз разный результат.
2.json:
PROCESSED: 5 objects in 647.78µs, 0 errors found
-------------------------------
#0: debt: 2166
companies: {"Казачий спас", "Святой престол"}
phones: {"345678", "666", "234567"}
-------------------------------
#1: debt: 3750
companies: {"Шестерочка", "Первая коллекторская"}
phones: {"789", "412", "788", "123", "12412", "2128506", "456"}
-------------------------------
#2: debt: 800
companies: {"Рога и копыта"}
phones: {"123", "234", "456"}
Дело не в выводе.
$ time taskset -c 0 ./target/release/habr -f 1.json
1.json:
PROCESSED: 1000000 objects in 825.626717ms, 0 errors found
-------------------------------
#0: debt: 910000000
companies: {"Рога и копыта", "Первая коллекторская", "Шестерочка"}
phones: {"789", "788", "123", "234", "2128506", "456"}
-------------------------------
#1: debt: 433200000
companies: {"Казачий спас", "Святой престол"}
phones: {"345678", "666", "234567"}
real 0m0,828s
user 0m0,790s
sys 0m0,038s
А в этом. Какой смысл сравнивать однопоточную и многопоточную версию? Сравнивайте однопоточные. Хотите сравнить многопоточные? Сравнивайте их.
Да и сравнение идёт крайне наивно, ведь нужно учитывать ещё и cputime. В условиях реального применения всегда будут желающие потратить cputime.
User time (seconds): 1.15
User time (seconds): 0.48
Какой в этом смысл?
Он не бинарный формат в том смысле в котором понимают бинарным форматы. Формально да, все форматы являются бинарными.
Основных отличий много. В бинарном формат строгий/однозначный, а значит в данных ненужно хранить структуру. У вас же оно хранится. Бинарный формат компактней. Ненужно ничего никуда преобразовывать(строки в числа, числа в строки и прочее). Подобные форматы заранее спроектированы таким образом, что-бы хранить данные в наиболее удобной для обработки вида. Про блочность вы уже сказали. Продолжать можно долго.
Нужно сравнивать. К тому же БД не обязательно должна быть реляционной. Аналогично, сомневаюсь, что кто-то в бигдате использует json. Для хранения отдельных документов? Возможно. Но то отдельные документы. К тому по эти документам строятся внешние индексы/иерархия.
Нет, но близко. ~0.7 против ~0.55. Нужно больше потоков.
Нет, почти в 2раза медленнее.
Мой код откровенно позорный. Так же я не заниматься оптимизациями. Если вам нужно быстро читать/писать/считать — вы явно выбрали не тот формат хранения и алгоритм. Используйте базу данных для таких задач.
Я примерно предполагаю как ускорить С++-версию ещё раза в 2. Но если использовать бинарный, строгий формат данных — можно получить результат в разы быстрее текущих даже для самой базовой, не оптимизированной версии. Мы просто тратим силы впустую.
real 0m1,611s — rust(старый)
real 0m0,963s — rust(новый)
У вас:
1.6 — luajit
1.3 — rust(новый)
1.288 — коэффициент между нами.
Если поделить ваши результаты на коэффициент, то мы получим 1sec(для rust новый). Что соотносится с моими измерениями.
Версия С++ у вас должна выполнятся примерно 0.71sec. Rust(старый) должна выполняться примерно 2sec.
Я адаптирую код под gcc8.
$ time ./target/release/habr -f 1.json
1.json:
PROCESSED: 1000000 objects in 961.843328ms, 0 errors found
-------------------------------
#0: debt: 910000000
companies: {"Рога и копыта", "Первая коллекторская", "Шестерочка"}
phones: {"789", "788", "123", "234", "2128506", "456"}
-------------------------------
#1: debt: 433200000
companies: {"Казачий спас", "Святой престол"}
phones: {"345678", "666", "234567"}
real 0m0,963s
user 0m0,958s
sys 0m0,005s
$ time ./target/release/habr -f 1.json
1.json:
PROCESSED: 1000000 objects in 998.797718ms, 0 errors found
-------------------------------
#0: debt: 910000000
companies: {"Рога и копыта", "Первая коллекторская", "Шестерочка"}
phones: {"789", "788", "123", "234", "2128506", "456"}
-------------------------------
#1: debt: 433200000
companies: {"Казачий спас", "Святой престол"}
phones: {"345678", "666", "234567"}
real 0m1,000s
user 0m0,994s
sys 0m0,006s
mmap(NULL, 99000002, PROT_READ, MAP_SHARED, 3, 0) = 0x7fd176971000
Попробуйте изменить флажки на «как у меня». Здесь mmap нужен не столько для скорости, а сколько для уменьшения потребления памяти. PROT_READ + MAP_POPULATE он сразу мапит пейджкеш в юзерспейс. Без каких-либо аллокация и копирования. В данном случае файл не используется весь, а читается последовательно. Эффект с zero-copy и отсутствия раздувания памяти будет заметен мало.
$ time luajit main.lua
done 1.241739 sec.
[{"debt":910000000,"companies":{"Шестерочка":true,"Рога и копыта":true,"Первая коллекторская":true},"phones":{"788":true,"456":true,"789":true,"2128506":true,"123":true,"234":true}},{"debt":433200000,"companies":{"Святой престол":true,"Казачий спас":true},"phones":{"234567":true,"666":true,"345678":true}}]
real 0m1,242s
user 0m1,173s
sys 0m0,069s
$ time ./target/release/habr -f 1.json
1.json:
PROCESSED: 1000000 objects in 1.609574904s, 0 errors found
-------------------------------
#0: debt: 910000000
companies: {"Первая коллекторская", "Шестерочка", "Рога и копыта"}
phones: {"788", "123", "456", "234", "2128506", "789"}
-------------------------------
#1: debt: 433200000
companies: {"Святой престол", "Казачий спас"}
phones: {"234567", "345678", "666"}
real 0m1,611s
user 0m1,599s
sys 0m0,010s
13.48% habr habr [.] habr::main
8.54% habr libc-2.29.so [.] _int_free
5.91% habr habr [.] serde_json::value::de::<impl serde::de::Deserialize<'de> for serde_json::value::Value>::deserialize
5.13% habr libc-2.29.so [.] malloc
4.89% habr libc-2.29.so [.] realloc
3.79% habr libc-2.29.so [.] _int_malloc
3.70% habr habr [.] <serde_json::read::SliceRead<'a> as serde_json::read::Read<'a>>::parse_str
3.28% habr habr [.] <std::collections::hash::map::DefaultHasher as core::hash::Hasher>::write
3.05% habr libc-2.29.so [.] _int_realloc
2.99% habr habr [.] <serde_json::value::de::<impl serde::de::Deserialize<'de> for serde_json::value::Value>::deserialize::ValueVisitor as serde::de::Visitor<'de>>::visit_map
2.97% habr habr [.] std::collections::hash::table::make_hash
2.92% habr habr [.] core::str::run_utf8_validation
2.86% habr libc-2.29.so [.] __memmove_avx_unaligned_erms
2.80% habr libc-2.29.so [.] cfree@GLIBC_2.2.5
2.32% habr habr [.] <alloc::collections::btree::map::BTreeMap<K, V>>::insert
2.21% habr libc-2.29.so [.] __memcmp_avx2_movbe
65,31% habr
34,07% libc-2.29.so
0,59% [unknown]
0,03% ld-2.29.so
22,67% cjson.so
Он мало что даёт в этом случае.
Предложите автору изменения. Я не знаю как это сделать.
git clone https://github.com/Tencent/rapidjson.git
g++ main.cpp -std=c++2a -Irapidjson/include -Ofast -march=native -fwhole-program
$ time ./a.out 1.json
-------------------------------
#0: debt: 910000000.000000
companies: {Шестерочка, Рога и копыта, Первая коллекторская}
phones: {234, 789, 123, 456, 788, 2128506}
-------------------------------
#1: debt: 433200000.000000
companies: {Казачий спас, Святой престол}
phones: {345678, 666, 234567}
real 0m0,545s
user 0m0,498s
sys 0m0,047s
В любом дистрибутиве boost уже должен стоять. Если нет:
У меня эта версия(0.55 секунды) где-то в 3раза быстрее раста(1.6 секунды).