вторник, 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...