Как стать автором
Обновить

Комментарии 62

Наверное такие алгоритмы применимы в очень специфических случаях, когда быстродействие важнее понимания, что происходит?

С другой стороны, а часто ли вам нужно понимание, как конкретно работает функция from_chars, при условии, что там нет багов и не нужно это дебажить?

Ну если это уровень библиотеки (черный ящик), то нет.
Такие вещи выносятся в отдельную функцию, которая хорошо документируется и внутрь больше никто не заглядывает — она без побочных эффектов и отлаживается до совершенства за один раз. Разумеется, если пихать такую логику в середину другой сложной функции, то ничем хорошим это не кончится.

Ну сам алгоритм, допустим, понятен.
Откуда вывод про ограничения именно на парсинге — не очень.
Бенчмарки стандартных стримов и strtod есть, а где результат с оптимизациями?
Ведь стрим и функция имеют чуть больше логики и гарантий, чем цикл с битовой магией.
Сколько кода нужно написать, что с платформами, багами?
Опять же, никто не спорит о возможности обогнать стандартную библиотеку и её суб-оптимальности, но хотелось бы цифр и ресурсов на поддержку с интеграцией подобных вещей.

Бенчмарки стандартных стримов и strtod есть, а где результат с оптимизациями?
Он там закопан в середине текста, 3.6 Гб/сек, то есть в 73 раза быстрее начального варианта (50 Mb/сек).
Нет. Это был парсинг целых чисел.
Он закопан в заголовке поста: гигабайт в секунду, т.е. в 20 раз быстрее начального варианта.
НЛО прилетело и опубликовало эту надпись здесь
Автор утверждает, что обогнал не только стандартную, но и специализированные библиотеки:

В вашем графике числа с плавающей точкой, а у автора int64

У автора double, прямо в заголовке статьи.

Точно, ошибся.

Читать в память большими кусками (помапить в память), а затем кучу потоков парсинга, вуаля.
Редко когда нужно распарсить файл, целиком состоящий из чисел. Обычно это JSON, XML или что-нибудь аналогичное, чей парсинг не распараллелить.
Если это CSV то вполне можно =) Даже есть версия парсера с использованием видеокарты.
Смотря какой json и xml. Если состоит из повторющихся строчек, то вполне можно, spark такое умеет как раз, паралеля чтение на кластере.
Жёсткие диски запросто обеспечивают последовательное чтение со скоростью в несколько ГБ/с,

простите, а вы не могли бы указать, что это за диски такие?
или имеете в виду топовые NVMe SSD, а не шпиндельные HDD?

Почему обязательно топовые? Первый нагуглившийся Samsung 970 EVO выдаёт 3.4 ГБ/с и стоит всего 13т.р. за 1ТБ. 3.4 — вполне себе "несколько". А топовые уже добрались до 7ГБ/с.

Ну то есть надо было с оговоркой писать, что жесткие диски именно SSD, а не HDD, причем подключаемые именно по NVME (т.к. у SATA SSD скоростей выше 500-600 МБ/c я что-то не встречал). Слово "запросто" было точно лишнее.

подключаемые именно по NVME

Нет, не именно. OCZ RevoDrive 3 X2, например, обеспечивает 1.5 ГБ/c при последовательном чтении, но при этом ни в каком месте не является NVMe. Поступил в продажу 10 лет назад.

Слово «запросто» было точно лишнее.

Привет из 2021 года. Тут можно запросто пойти в любой компьютерный магазин и запросто купить NVMe SSD. Это не какая-то экзотика, доступная только избранным или за чемодан денег.
Это всё понятно, но я например не уверен, что термин «жёсткий диск» полностью применим к «OCZ RevoDrive 3 X2» и прочим SSD.
Предлагаете называть их «жёсткий диск» и «твёрдый диск», чтобы не было путаницы?

Твердотельный накопитель

Или SSD. Ой, уже придумано.

Ок, да, формально вы правы и ничего в форме диска в SSD нет и это слово использовать некорректно. С другой стороны — а какой термин в русском языке эквивалентен "hard drive"?

Есть HDD (hard disc drive) и SSD (solid state drive). Русские аналоги: жёсткий диск и твердотельный накопитель вполне устоявшиеся и не ушли от оригинала.

Попробуйте прочесть вопрос ещё раз. Меня интересует собирательный термин для HDD+SSD.

Накопитель
Единственная сложность — что «твердотельный накопитель» вдвое длиннее, чем «solid state drive». С жёстким диском такой сложности нет.
Hard drive это исключительно HDD, то есть жесткий диск. Применять этот термин к твердотельным дискам не корректно.
Собирательный термин — накопитель, или storage device.
С другой стороны — а какой термин в русском языке эквивалентен «hard drive»?
Думаю, что жёсткий диск
Если докапываться до терминологии, то никакого привода в SSD тоже нет, так что называть его «drive» столь же некорректно. Называли бы его «solid state storage», было бы даже аллитеративнее =)

Наверное имелось в виду PCIe так как NVMe это спецификация а не интерфейс, но в современных реалиях почти гвоздями прибит к PCIe, а упомянутый RevoDrive напрямую на PCIe вешался, оттуда и 1.5 GB/s.

Думаю, предыдущий комментатор имел в виду подключение по PCIe, куда указанный диск и подключается (как и NVMe)

Речь о софте для высокоскростного парсинга, который явно гоняют на серверах, а не домашних ноутбуках. Еще 10 лет назад можно было набить 2U сервер 24 дисками sff sas, каждый давал 180-200 МБ/с а весь массив запросто давал 3-4 ГБ/с. Сейчас скорости намного выше.

Хм. Для тех кому надо много и быстро есть специальные форматы parquet.apache.org
А вот обычные текстовый файлы (особенно большие) могут содержать ошибки, появление которых может стать внезапным сюрпризом.

ps: Cжатые текстовый файлы можно прасить быстрее, т.к. требуется на порядок меньшая пропускная способность дисковой подсистемы для того-же объёма данных. И плюс есть хотя бы контрольная сумма.
В первом же абзаце объяснено, что пропускная способность дисковой подсистемы на один-два порядка выше скорости парсинга, так что сжатие его только замедлит.
НЛО прилетело и опубликовало эту надпись здесь
Отличная подборка, но к парсингу чисел никакого отношения не имеет.
В С++17 стандарте специально для парсинга чисел появилась std::from_chars (из \<charconv\>). Как утверждается она специально создана для скоростных парсеров, не использует всякие локали и должна работать на порядок быстрее. Вы случайно её скорость не замеряли?
Автор пишет, что std::from_chars парсит 140 МБ/с: втрое быстрее, чем потоки, и всемеро медленнее, чем его реализация.
The guarantee that std::from_chars can recover every floating-point value formatted by std::to_chars exactly is only provided if both functions are from the same implementation.

Кто-нибудь может объяснить, почему так?

Эти функции не только double и float парсят, а также целые в различных системах исчисления. Так куча флагов, перегрузок и не все из них еще на всех платформах имплементированы.
Так куча флагов, перегрузок и не все из них еще на всех платформах имплементированы.
перегрузки не дают накладных расходов в рантайме
Кто-нибудь может объяснить, почему так?
стандарт гарантирует что для любого double значения преобразование to_chars и последующее from_chars всегда вернет ровно то же самое число. Так что либо реализация автора не дает такой гарантии, либо он додумался до чего-то, до чего уже несколько лет не могли додуматься разработчики компиляторов. Ну или последний вариант, на который бы я поставил — автор сравнивал парсинг с std::chars_format::general, который предполагает что в double числе может быть экспонента. А обработка этого случая уже может и замедлить код.

Upd: посмотрел код бенчмарка (ссылка с гитхаба автора). Действительно, автор использует формат по умолчанию, который и есть std::chars_format::general. Ему бы стоило замерить с std::chars_format::fixed, и уже потом заявлять о 7-кратном приросте.

Upd2: перепроверил, у автора тоже параметризуется формат, в полной аналогии со стандартным. Возвращаемся к первым двум гипотезам.
Вы не поняли: цитата Playa — про std::from_chars и std::to_chars, а у ускоренного from_chars есть только одна реализация, содержащая только одну эту функцию, так что к ней замечание «if both functions are from the same implementation» никак не могло бы относиться.

Разработчики компиляторов в меньшей степени, чем автор, интересуются платформо-зависимыми оптимизациями, так что ничего удивительного.
Проблема в том, что std::from_chars/to_chars для float типов реализованы только в студии. Ни в clang ни в gcc их до сих пор нет — en.cppreference.com/w/cpp/compiler_support/17
В x64 их больше нет.
Эти извращения в середине статьи напомнили, как на Z80 обычными регистрами память очень медленно заполнялась, например для очистки кадра заполнить видеопамять нулями, но если поместить регистр стека на видеопамять, то команда работы со стеком POP очень быстро могла заполнить память нулями, что позволяло добиться 50fps в играх и демках на таком слабом железе.
Я думаю, что можно и быстрее, если теперь подключить к этой обработке еще инструкции векторизации (SIMD).
Не верю, что в данной задаче компилятор это сделает сам. Да и зачем полагаться на компилятор (который это не сделает), если то же самое можно сделать руками с гарантированным результатом?
На сколько я понял автор хотел сравнять скорость чтения 1Гб даблов с их парсингом. Дальнейшая оптимизация может привести к тому что мы уже не будем успевать читать их с диска с такой скоростью, ну или близко к этому.
Нет ничего зазорного, чтобы узким горлышком было чтение с диска, а не парсинг. В конце концов, этот файл может читаться из памяти, из сети, из RAID-массива. Тем более, что векторизация кода — это доступная техническая процедура. Так зачем сдерживать себя и не попробовать?
Я ни в коем случает не против. Ясно что пределы оптимизации не достигнуты: дальше подключаем все ядра в параллель, SIMD и т.д. Все это сильно увеличит скорость, но также и сильно усложнит код и ничего не даст с точки зрения алгоритма описанного в статье.
Так зачем сдерживать себя и не попробовать?

Чтобы не тратить $5000 на реализацию оптимизации, которая сэкономит $50.

Именно поэтому автор начинает с анализа скорости парсинга относительно скорости диска: если разница в сто раз, то ускорение парсера имеет большой практический смысл. Если разница в два-три раза, то это уже больше спортивный интерес.

Вот если векторизацию этого случая встроить прямо в GCC, тогда она может окупиться, даже если конкретно на этой задаче сэкономит очень мало.
Остался только вопрос — зачем был изначально записан файл в текстовом формате? Напоминает легендарные истории, как организация печатала грузовик отчетности, который ехал в налоговую, а там оцифровывался «взад»…

Или, почему используется десятичный формат записи double, а не шестнадцатиричный, в котором десятичный парсинг остаётся только для порядка.


Но так как могут быть тонны внешних причин, на которые нельзя влиять — задача вполне достойная.

К примеру, выгрузка БД из OpenStreetMap или сопоставимой по объему.

А еще у меня был заказчик из эквадора. У него таксопарк. Каждое такси три раза в секуду шлет отчет о состянии авто, вместе с GPS координатами. Объем пакета — где-то 10Кб. Когда количество машин прибилизилось к сотни, появились лаги парсера. В общем, там пришлось слегка поковыряться, но я гарантировал, что теперь проблемы возникнут когда количетсво машин приблизится к десятку тысяч :-)

На вопрос «а какого [ПИП] надо три раза в секуду»? Мне хозяин ответил: «Стреляют. Надо успеть разблокировать оружие до того, как водителя убьют».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий