Показаны сообщения с ярлыком haskell. Показать все сообщения
Показаны сообщения с ярлыком haskell. Показать все сообщения

пятница, 3 февраля 2017 г.

From Haskell to Standard ML

Фух... Полностью избавились от Haskell на серверах.

Причина: свойства MIO (Threaded RTS) избыточны для нас и сам MIO настолько сложен, что в настоящий момент не может быть без ошибок. А обычный RTS медленный и использует устаревший системный вызов select.

Сопутствующий минус: кода стало в 2 раза больше,
но есть еще один плюс: скорость выросла 2 раза.

P.S. Про ошибку в MIO не скажу - это будет маркер.

пятница, 9 декабря 2016 г.

Standard ML, Haskell parser benchmark

https://github.com/kni/sparcl

Redis protocol benchmark (seconds):
          String  Substring  Reader-String  Reader-Substring   Attoparsec
Haskell:          4.63                                         7.98
Mlton:    3.07    1.81       1.90           1.50
PolyML:   6.32    4.43       7.86           6.08


CSV benchmark (seconds):
          String  Substring  Reader-String  Reader-Substring   Attoparsec
Haskell:          7.85                                         14.41
Mlton:    6.91    5.61        3.51          3.10
PolyML:   9.31    9.29       10.56          9.14

P.S. Haskell ByteString - is Substring

But Substring has no apparent advantages over String in a real application (https://github.com/kni/redis-sharding-sml) :-)

четверг, 1 декабря 2016 г.

От Haskell к Standard ML

Переписал сервер с Haskell на Standard ML (MLton и Poly/ML).

Кода стало больше, скорость стала выше. Быстрей не только MLton версия, но и Poly/ML. И это, несмотря на то, что у PolyML медленный FFI.

Кода в Standard ML версии больше чем в Haskell потому, что в Haskell используются "зеленые треды" и менеджер IO, а в Standard ML - "голый" kevent (epoll) и функции обратного вызова. Да и синтаксис компактней в Haskell, но в Standard ML "модульней".

В целом, хотя в Standard ML версии кода больше и используются функции обратного вызова, она не сильно сложней Haskell версии. Сложность какбы размазана равномерно. А в Haskell сложность скрывается за "простыми" вещами. Haskell требует больше знаний и имеет больше нюансов, когда копнеш глубже.

Если коротко, то в сравнении с Standard ML Haskell выигрывает в малом, но проигрывает в большом.

P.S.
Да, еще в GHC MIO есть баг, который лимитирует использование Haskell версии.

понедельник, 28 ноября 2016 г.

Haskell: ошибка в Mio...

Кажется, в Mio ("Scalable IO Manager") есть ошибка.
Mio - это IO менеджер для threaded режима в GHC 7.8 и выше.
В сервере, при превышении определенного размера данных, происходит зависание треда.
Mio считает, что писать в сокет еще нельзя, а на самом деле можно.
Проверил на GHC 7.8.[34] и 8.0.1.
В не-threaded режиме зависания нет, все работает.

А ведь считал, что основное преимущество Haskell - это "зеленые нитки" с многопроцессорностью при высокой скорости. А тут, оказывается...

вторник, 1 ноября 2016 г.

Whole program optimization

Не хватает в GHC whole program optimization.
Есть "data type" и две функции. Одна возвращает по входным данным этот "data type", другая его используется для разветвления логики.
Перенес определение data type и первую функции в другой файл - производительность сервера упала на 20%.

вторник, 13 сентября 2016 г.

Hans Boehm's GC benchmark - не только для GC

Hans Boehm написал бенчмарк для GC (работа с Tree).
Это бенчмарк есть и в "NoFib: Haskell Benchmark Suite"
https://github.com/jyp/nofib/blob/master/gc/gc_bench/gc_bench.hs

Немного модифицировал его, чтобы была возможность задавать kStretchTreeDepth.
А также сделал порт на Standard ML (один в один).

Запускаем его с большим размером кучи, чтобы GC не срабатывал, то есть используем как Tree бенчмарк.

GHC version 7.8.3 (ghc -O2 -rtsopts ...)
> time ./gc_bench_haskell 24 16 500000 16 22 +RTS -H250M
13.961u 0.220s 0:14.47 97.9% 947+248k 1+0io 27pf+0w

Poly/ML 5.6
> time ./gc_bench_polyml -H250 24 16 500000 16 22
5.388u 0.389s 0:05.58 103.2% 551+200k 0+0io 0pf+0w

MLton 20130715
> time /net/bsd/gc_bench_mlton 24 16 500000 16 22
2.593u 0.000s 0:02.61 99.2% 212+181k 0+0io 3pf+0w

multiMLton
> time ./gc_bench_multiMLton 24 16 500000 16 22
2.855u 0.000s 0:02.89 98.6% 273+197k 0+0io 0pf+0w


Выходит, что Haskell быстр при работе с числами, векторами, строками, а работа со сложными структурами данных не его конек?

P.S.
На http://benchmarksgame.alioth.debian.org в binary-trees бенчмарке Haskell обгоняет OCaml. Но там Haskell работает параллельно на 4 ядрах, а OCaml на одном.

Lazy ByteString или Haskell - кому сказать прощай?

Наверное вы уже знаете, что у меня есть 3 версии RedisSharding: Perl версия, Haskell версия с использованием Lazy ByteString и Strict Haskell версия с использованием attoparsec.

В старой заметке Redis Sharding: Lazy vs Strict (http://by-need.blogspot.com/2013/01/rs.html) утверждается, что Lazy и Strict версии примерно одинаковы по производительности.

Но, так как RedisSharding используется в основном для больших MGET команд, сравнение Lazy и Strict версии в pipeline режиме не производилось. А вот сейчас как-то случайно...

Оказалось, что Lazy версия в pipeline режиме имеет очень маленькую производительность.

cpuset -l 0 ./redis_sharding --port=8090 --nodes=127.0.0.1:8081,127.0.0.1:8082,127.0.0.1:8083,127.0.0.1:8084 +RTS -s -N1 -A10M -qa


Lasy версия.

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 1
SET: 20000.00 requests per second
GET: 19801.98 requests per second
MSET (10 keys): 9891.20 requests per second

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 2
SET: 195.46 requests per second
GET: 196.31 requests per second
MSET (10 keys): 196.29 requests per second

Strict версия.

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 1
SET: 16103.06 requests per second
GET: 15898.25 requests per second
MSET (10 keys): 9337.07 requests per second

> redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 2
SET: 25906.74 requests per second
GET: 25839.79 requests per second
MSET (10 keys): 12285.01 requests per second


Первой мыслью было, что где-то что-то не так сделал в Lasy версии. Но результаты профилирования опровергают это, тем, что нет какой-то явной медленной функции:

Lasy версия
COST CENTRE                           MODULE                               %time %alloc
throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal               29.8    0.7
MAIN                                  MAIN                                  11.9    0.8
server_responses                      RedisSharding                         11.2    0.9
client_reader                         RedisSharding                         10.1    5.6
send                                  Network.Socket.ByteString.Lazy.Posix   9.5    8.1
...
key2server                            RedisSharding                          2.3    0.4

Strict версия
COST CENTRE                           MODULE                              %time %alloc
endOfLine                             Data.Attoparsec.ByteString.Internal  13.0    7.5
decimal                               Data.Attoparsec.ByteString.Char8     11.0    8.2
servers_sender                        RedisSharding                        10.6   13.1
throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal              10.4    0.5
client_reader                         RedisSharding                         9.8    5.5
...
key2server                            RedisSharding                         1.7    1.2

Но где тут 20 кратное замедление? Возьмем для ориентира чистую функцию key2server, которотая под ключу определяет на какою ноду отправлять данные. В обоих версиях время выполнения ее в процентной отношении соизмеримо. Значит профайлер не показывает обнаруженное 20 кратное замедление. О чем это может говорить? Неужели, причина кроется где то глубоко в Haskell RTS? Что, выходит, Харпер прав?

P.S. Для не threaded режима работы RTS Haskell, цифры аналогичны.

P.S.S. Нужно для объективности еще провести исследования.

среда, 11 июня 2014 г.

Mio benchmark (GHC 7.8)

Наконец-то добрался протестировать производительность Mio ("Scalable IO Manager"), который появился в GHC 7.8. Предыдущей менеджер при переключении в threaded режим существенно терял производительность.

Тестирование производилось, используя реальное приложения, написанное на Haskell: RedisShardnig (http://github.com/kni/redis-sharding-hs-strict). Оно собиралась при помощи команды:

ghc -threaded -feager-blackholing -rtsopts -O2 --make redis_sharding.hs

Результаты сравнения Mio и старого менеджера.

FreeBSD (мой ноутбук: Intel(R) Pentium(R) CPU P6200)
Requests per second
          |       | threaded
----------------------------
GHC 7.8.1 | 11560 | 12853
GHC 7.6.1 | 11210 |  7686

Linux (какой-то сервер c 8 ядерами)
Requests per second
          |       | threaded
----------------------------
GHC 7.8.1 | 16393 | 17605
GHC 7.6.1 | 16583 |  9354

А вот данные как изменяется производительность с ростом количество рабочих OS threaded (параметр запуска -N):

GHC 7.8.1 threaded, Linux, 8 ядер.
... +RTS -N1 ( 4 OS threads) - 15873.02 requests per second
... +RTS -N2 ( 6 OS threads) - 28735.63 requests per second
... +RTS -N3 ( 8 OS threads) - 29411.77 requests per second
... +RTS -N4 (10 OS threads) - 26954.18 requests per second

Как видим, с выходом нового менеджера ввода-вывода производительность не только перестала падать при переключении в threaded режим, а даже стала возрастать!

воскресенье, 1 сентября 2013 г.

Ждем Mio в GHC 7.8

Когда в GHC 7.0 появился "Scalable IO Manager", то стало возможным писать на Haskell TCP/IP сервера и использовать их в реальных условиях.

По скорости работы с текстовыми данными (ByteString) и списками GHC почти не уступает OCaml, Mlton, manticore(http://manticore.cs.uchicago.edu/), PolyML проигрывает 20%. Но GHC имеет лучший GC, чем у OCaml и Mlton, а manticore находиться пока в разработке.

Все бы было хорошо, но вот реализация самого "Scalable IO Manager" не оптимальная и приводит почти к двукратному снижению производительности. В связи с этим стал смотреть в сторону multiMLton и manticore, а также PolyML. Оценил насколько быстро можно к их менеджерам легковесных потоков добавить IO менеджеры, и понял, что трата времени на это будет для меня не рациональна.

Одновременно с этим узнал, что производительность существующего в GHC IO Manager не устраивает не только меня и нашлись люди, которые сделали
Mio: A High-Performance Multicore IO Manager for GHC.

Что-ж, ждем выхода GHC 7.8, который будет включать в себя Mio...

вторник, 26 февраля 2013 г.

Haskell - энергичный лентяй

Какая то ленивость у Haskell не правильная, слишком энергичная. :-)
Да, ленивость - это дело не простое, тут уметь надо.

Возьмем для примера функцию foldl. Ну какой лентяй будет как сумасшедший создавать столько чанков, что стек переполняется. На это способна только бешеная лисица, которой сотня километров - не крюк, на и Haskell.

Вы можете возразить, что вот ленивые списки - это окно в бесконечность. Да любая комбинация из функций map свалила бы Haskell в эту бесконечность, если бы не тщательно прописанные правила преобразования различных комбинаций функций. Да и вместо длинных списков предпочтительней использовать Stream Fusion.

Ну что это за ленивый язык, которому надо говорить, как правильно лениться?

Возьмем ленивые строки. Ну какие они ленивые!

Разве ленивая строка при добавлении данных в конец будет как бешеная лисица пробегать по всем чанкам? Нет, она сохранит данные в некотором буфере и добавит лишь когда эти данные понадобятся.

Ну а когда в результате некоторой операции данные в первом чанке заканчиваются, так называемая ленивая строка энергично переходит к следующей порции данных, например, прочитай их из сокета. Ну не торопись, поленись немного, дай определить, что будет чтение новой порции данный!

Что тут можно сказать? Лень - это не просто. Правильно лениться - уметь надо!

среда, 6 февраля 2013 г.

RedisShardnig и GHC threaded

В последней версии RedisShardnig (http://github.com/kni/redis-sharding-hs-strict) буферизация данных и отправка их в сокеты происходит в отдельных user-lavel threads (forkIO), которым данные передаются посредством MVar переменных.

Разумеется, использование MVar имеет накладные расходы, но какие? Может, не смотря на некоторое усложнение кода, выгодней буфера таскать с собой?
Набросал простенький тестик, и кроме MVar добавил также RefIO. Тест показал, что и MVar и RefIO очень быстры и их можно использовать без опаски замедлить скорость выполнения программ.

Но тут я заметил, что компилировал без включения threaded ражима. А этот режим нужен, чтобы задействовать kqueue и epoll.
Скомпилировал я тесты в threaded режиме, запустил и увидел, что следует минимизировать их использование.

Но каждый тест является синтетическими, поэтому я решил вязать реальное приложение (RedisShardnig) и сделать версию с минимальным использованием MVar. В этой версии для буферизации не будут использоваться отдельные user-lavel threaded, с которыми общение происходит посредством MVar, а все буферизация будет происходить в рабочих threads и буыера будет таскаться с собой. Конечно множество MVar все равно останется, ведь при каждой операции записи чтения данных из сокетов используются MVar.

Текущая версия RedisShardnig (в таблице обозначена как mvar) и новая версия (в таблице обозначена как self) были собраны без и с поддержкой threaded режима и протестированы на производительность при помощи redis-benchmark (в 10 потоков). За RedisShardnig находилось 4 Redis node. Процессор имел 8 ядер. В таблице приведены тысячи SET запросов в секунду.

                 -P 1  -P 100
mvar             19      83
mvar threaded    10      93
self             23     116
self threaded    13     107

mvar -N2         21     166
self -N2         26     208

mvar -N4         23     232
self -N4         25     263


-P 100 - обозначает режим Pipeline с 100 команд в одном пакете (смотри redis-benchmark). То есть в этом режиме в 100 раз снижено количество отправки и получения данных из сокета.

Из таблицы видно, что для приложений с интенсивной сетевой нагрузкой включение threaded режима снижает производительность в 2 раза.
Впрочем и для приложений с другим характером нагрузки также заметно снижение производительность при использование threaded режима.
А не использовать threaded режим нельзя, так как для сетевых приложений необходим kqueue и epoll.
Кстати, разработчики GHC игнорируют это упущение.

четверг, 31 января 2013 г.

Haskell Lazy vs Strict ByteString for Network

У привязки ленивой строки с сокету при помощи getContents есть один недостаток.
Невозможно определить есть ли еще данные во входном буфере.

А это иногда очень важно знать. Например, чтобы сделать оптимальную буферизацию.

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

пятница, 25 января 2013 г.

RedisSharding Benchmark

Let's go on testing RedisSharding, but this time we'll take redis-benchmark as the client. The conditions are the same.
Port:
6380 Redis (1 redis-server)
8000 RedisSharding (for 4 nodes)
9000 twemproxy (nutcracker) (for 4 nodes)

./redis_sharding --port=8000 --nodes=127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384 +RTS -s -N4 -A10M -qa

> redis-benchmark -p 8000 -n 10000 -c 10 -q -t set,get
SET: 24038.46 requests per second
GET: 24038.46 requests per second

> redis-benchmark -p 9000 -n 10000 -c 10 -q -t set,get
SET: 69444.45 requests per second
GET: 58823.53 requests per second
And now we'll enable the Pipeline mode and will be increasing the number of commands in one Pipeline gradually.
> redis-benchmark -p 8000 -n 10000 -c 10 -q -t set,get -P 100
SET: 217391.30 requests per second
GET: 250000.00 requests per second

> redis-benchmark -p 9000 -n 10000 -c 10 -q -t set,get -P 100
SET: 277777.78 requests per second
GET: 312500.00 requests per second
However, RedisSharding uses all the 4 core sets while the twemproxy is one-thread.
Let's launch the client with one connection:
> redis-benchmark -p 8000 -n 10000 -c 1 -q -t set,get -P 100
SET: 70422.54 requests per second
GET: 76335.88 requests per second

> redis-benchmark -p 9000 -n 10000 -c 1 -q -t set,get -P 100
SET: 227272.73 requests per second
GET: 263157.91 requests per second
But why should we abandon usage of all the processor's core sets?
> redis-benchmark -p 6380 -n 10000 -c 20 -q -t set,get -P 1000
SET: 256410.25 requests per second
GET: 232558.14 requests per second

> redis-benchmark -p 8000 -n 10000 -c 20 -q -t set,get -P 1000
SET: 147058.83 requests per second
GET: 151515.16 requests per second

> redis-benchmark -p 9000 -n 10000 -c 20 -q -t set,get -P 1000
SET: 129870.13 requests per second
GET: 100000.00 requests per second
We are increasing it more and we see that with such size of Pipeline it is Redis itself giving up.
> redis-benchmark -p 6380 -n 10000 -c 20 -q -t set,get -P 10000
SET: 17421.60 requests per second
GET: 21834.06 requests per second
While RedisSharding, judging from the above information, is perfectly fine:
> redis-benchmark -p 8000 -n 10000 -c 20 -q -t set,get -P 10000
SET: 13280.21 requests per second
GET: 19011.41 requests per second
What can't be said of nutcracker.
> redis-benchmark -p 9000 -n 10000 -c 20 -q -t set,get -P 10000
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
Writing to socket: Connection reset by peer
SET: 17064.85 requests per second
zmalloc: Out of memory trying to allocate 335799 bytes
Abort



> nutcracker-0.2.2/src/nutcracker -c twemproxy/nutcracker0.yml
[Fri Jan 25 09:36:19 2013] nc.c:177 nutcracker-0.2.2 started on pid 19962
[Fri Jan 25 09:36:19 2013] nc.c:181 run, rabbit run / dig that hole, forget the sun / and when at last the work is done / don't sit down / it's time to dig another one
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_util.c:223 malloc(16384) failed @ nc_mbuf.c:46
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 8 failed: Connection reset by peer
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 9 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 10 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 11 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 12 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 13 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 14 failed: Broken pipe
[Fri Jan 25 09:38:28 2013] nc_connection.c:384 sendv on sd 15 failed: Broken pipe

Redis: SET vs MSET; Sharding: twemproxy vs RedisSharding

It's logical that one MSET command with multiple keys is faster than multiple SET commands. The question is: how much faster when using real clients? What is more rational for the data sharding? twemproxy or RedisSharding? The first one is very fast, the second one supports MSET, MGET commands properly.

Let's take these clients: http://search.cpan.org/~melo/Redis-1.955 (Simple client) and http://search.cpan.org/~dgl/AnyEvent-Redis-0.24 (Event client).

"No Mutly Keys" test was run SET command for 10 keys, then GET for this keys, and then DEL.
"Yes Mutly Keys" test was run MSET with 10 keys, then MGET with this keys, and then DEL.
This series of commands was run 1000 time for each test.

Version:
Redis         - 2.4.15 (http://redis.io)
nutcracker    - 0.2.2 (http://github.com/twitter/twemproxy)
RedisSharding - 1.0 (http://github.com/kni/redis-sharding-hs-strict)
twemproxy and RedisSharding used 4 Redis nodes.
Clients, Redis nodes, twemproxy, RedisSharding were run on one computer.
The comparison includes the data for the isolated Redis server.

Average time of executed test scripts:
Client | Mutly Keys | Redis | twemproxy   | RedisSharding
---------------------------------------------------------
Simple |   No       | 02.72 | 03.93       | 05.69
Event  |   No       | 03.65 | 03.54       | 03.54
Simple |   Yes      | 00.86 | no support* | 01.34
Event  |   Yes      | 00.61 | no support* | 00.75
* - twemproxy supports MSET basing on the hash tag only, while we are interested in proper full support! https://github.com/twitter/twemproxy/issues/51#issuecomment-12257220

And now, let's take the faster client (http://hackage.haskell.org/package/hedis):

Keys | Mutly Keys | Redis | twemproxy   | RedisSharding
-------------------------------------------------------
 10  |   No       | 00.08 | 00.13       | 00.26
 10  |   Yes      | 00.02 | no support* | 00.14
100  |   No       | 00.78 | 01.34       | 02.56
100  |   Yes      | 00.30 | no support* | 00.90 !!!
Conclusions:
If you don't have to use Mutly Keys commands, you can choose twemproxy for sharding.
However, if you have an opportunity to use MSET and MGET, it's worth trying RedisSharding.

вторник, 8 января 2013 г.

Redis Sharding: Lazy vs Strict

Так как у Redis команда KEYS достаточна требовательна к CPU, а сам Redis - однопоточен, где-то два года назад для одного проекта меня попросили распараллелить запросы к Redis на несколько экземпляров (node), по одному на каждое ядро CPU.

Так появился Redis Sharding (https://github.com/kni/redis-sharding). Он был написан на Perl. Но так как Perl не отдает память назад OS, при обработке команд MSET с большим количеством длинных ключей и значений процесс Redis Sharding разрастался в размерах и оставался таким.

В связи с этим появилась Haskell версия Redis Sharding (https://github.com/kni/redis-sharding-hs). Она не только красиво работала с памятью, но была быстрей в 2-4 раза.

Haskell версия была основана на Data.ByteString.Lazy. Ленивы строки очень удобны в работе. Ведь у нас есть как-бы все данные сразу, а фактически они подчитываются из сокета по мере необходимости. Однако что-бы отправкой данных из ленивой строки в сокет, создается массив из обычных ByteString, для которых и вызывается системный вызов writev. Именно эти дополнительные выделения памяти и копирование данных из Lazy ByteString в Strict ByteString существенно снижали производительность Redis Sharding.

И так, спустя два года, я решил попытаться ускорить Redis Sharding, используя Strict версию ByteString и Data.Attoparsec. Так под новогодние праздники появилась Strict версия Redis Sharding (https://github.com/kni/redis-sharding-hs-strict).

Однако существенно увеличения производительности добиться не удалось.
Strict версия на маленьких данных быстрей до 20% и в несколько раз меньше занимает памяти, но с ростом размера данных, это преимущество все уменьшается, а потребление памяти возрастает и перегоняет Lazy версию.

По видимому, это происходит из-за того, что при парсинге иногда происходит объединение нераспарсенного хвоста со следующим входным буферов.

Если сделать размер входного буфере не 4k, а 32k (это размер defaultChunkSize для Data.ByteString.Lazy), то скорости одинаковые, но Strict версия все еще использует меньше памяти (на 30%), но с ростом размера данных lazy версия начинает потреблять меньше памяти.


Вывод.
Когда размеры порций данных меньше, чем размер оптимального буфера, то предпочтительней Strict версия. Иначе - Lazy.