Когда я использую ByteString, а когда нет?
Я делал довольно плохие попытки проблемы PRIME1 на SPOJ. Я обнаружил, что использование ByteString действительно помогло производительности для чтения в тексте проблемы. Однако использование ByteString для вывода результатов на самом деле немного медленнее, чем использование функций Prelude. Я пытаюсь понять, если я делаю это неправильно, или если это ожидается.
Я провел профилирование и время, используя (putStrLn.show) и эквиваленты ByteString тремя разными способами:
- Я проверяю каждого кандидата, чтобы узнать,
является простым. Если это так, я добавляю его в список
и напишите его (putStrLn.
шоу)
- Я делаю список всех простых чисел
и запишите список, используя
(putStrLn. unlines. show)
- Я делаю список всех простых чисел
и запишите список, используя
map (putStrLn. show)
Я ожидал, что числа 2 и 3 будут выполняться медленнее, когда вы создаете список в одной функции и потребляете его в другом. При печати чисел по мере их создания я избегаю выделения какой-либо памяти для списка. С другой стороны, вы вызываете системный вызов вызова с каждым вызовом putStrLn. Правильно? Итак, я тестировал, а №1 был самым быстрым.
Наилучшая производительность была достигнута с опциями # 1 и Prelude ([ Char]). Я ожидал, что моя лучшая производительность будет вариантом № 1 с ByteString, но это было не так. Я использовал только ленивый ByteStrings, но я не думал, что это имеет значение. Будет ли это?
Некоторые вопросы:
- Вы ожидаете, что ByteStrings
лучше работать для написания
Целые числа в stdout?
- Я пропустил шаблон пути
генерировать и записывать ответы
что приведет к улучшению
производительность?
- Если я только пишу цифры,
текст, когда, если когда-либо, есть
извлечь выгоду из использования ByteString?
Моя рабочая гипотеза заключается в том, что запись Integer с ByteString происходит медленнее, если вы не комбинируете их с другим текстом. Если вы комбинируете целые числа с [ Char], вы получите лучшую производительность, работающую с ByteStrings. I.e., ByteString переписывает:
putStrLn $ "the answer is: " ++ (show value)
будет намного быстрее, чем версия, написанная выше. Это правда?
Спасибо за чтение!
Ответы
Ответ 1
Выполнение массового ввода обычно выполняется быстрее с помощью байтов, поскольку данные плотны, а просто меньше данных для перетасовки с диска в память.
Запись данных как результат, однако, немного отличается. Как правило, вы сериализуете структуру, генерируя много мелких записей. Таким образом, плотные, объемные записи об ошибках не помогут вам в этом случае. Даже обычный Strings
будет делать разумно при инкрементном выходе.
Однако все не потеряно. Мы можем восстанавливать быстрые массивные записи, эффективно наращивая bytestrings в памяти. Этот подход используется различными пакетами *-builder
:
Вместо того, чтобы преобразовывать значения в множество крошечных байтов, и записывая их по одному, мы передаем преобразование в постоянно растущий буфер и, в свою очередь, записываем этот буфер в одну большую часть. Это приводит к значительному издержкам ввода-вывода и увеличению производительности (часто signficant) над строкой ввода-вывода.
Такой подход применяется, например, webservers в Haskell или эффективная система HTML, blaze.
Кроме того, производительность даже при объемной записи будет зависеть от эффективности любой функции преобразования, которую вы имеете между вашими типами и байтами. Для Integer
вы можете просто копировать битовый паттерн в памяти для вывода или вместо этого использовать неэффективный декодер. В результате вам иногда приходится немного думать о качестве используемой функции кодирования, а не только о том, следует ли использовать Char/String или bytestring IO.
Ответ 2
Обратите внимание, что производительность не является основным различием между ByteString
и String
. Первый - для двоичных данных, а последний - для текста Unicode. Если у вас есть двоичные данные, используйте ByteString
, если у вас есть текст в Юникоде, используйте тип Text
из текстового пакета .