![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Haskell не перестает удивлять меня!
Есть старый проект https://github.com/kni/redis-sharding-hs-strict.
В нем для определение типа команды используется поиск по списку пар:
https://github.com/kni/redis-sharding-hs-strict/blob/master/RedisSharding.hs#L59
Давном-давно
thesz посоветовал заменить это на дата-тип.
Заменил на:
Ожидал рост производительности, но получил ухудшение на 20-25%!
Измерял при следующих условиях, и, разумеется несколько раз и разной очередности:
Вот результат профилирования (при профилировании замедление меньше).
Старая версия:
А вот - новая с data CmdType:
Почему?!
UPDATE.
Загвоздка была, в том, что cmdType, так как много строк, поместил в другой файл, а не в тот, где case cmdType cmd of
И Haskell не смог оптимизировать. Когда все слил в один - скорость вернулась!!!
Есть старый проект https://github.com/kni/redis-sharding-hs-strict.
В нем для определение типа команды используется поиск по списку пар:
https://github.com/kni/redis-sharding-hs-strict/blob/master/RedisSharding.hs#L59
Давном-давно
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
Заменил на:
data CmdType = CmdToAll -- На все сервера | CmdToOne -- На конкретные сервер | CmdToMany -- На множество серверов. CMD key1 ... keyN | CmdToManyValues -- На множество серверов. CMD key1 value1 ... keyN valueN | CmdToManyTimeout -- На множество серверов. CMD key1 ... keyN timeout (блокирующие команды) cmdType "PING" = Just CmdToAll cmdType "AUTH" = Just CmdToAll ....
Ожидал рост производительности, но получил ухудшение на 20-25%!
Измерял при следующих условиях, и, разумеется несколько раз и разной очередности:
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 redis-benchmark -p 8090 -n 10000 -c 10 -q -t set,get,mset -P 10
Вот результат профилирования (при профилировании замедление меньше).
Старая версия:
COST CENTRE MODULE %time %alloc throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal 12.2 0.5 endOfLine Data.Attoparsec.ByteString.Internal 11.5 7.5 servers_sender RedisSharding 10.0 13.2
А вот - новая с data CmdType:
COST CENTRE MODULE %time %alloc cmdType RedisParser 12.5 12.4 throwSocketErrorIfMinus1RetryMayBlock Network.Socket.Internal 11.9 0.4 endOfLine Data.Attoparsec.ByteString.Internal 9.2 6.4
Почему?!
UPDATE.
Загвоздка была, в том, что cmdType, так как много строк, поместил в другой файл, а не в тот, где case cmdType cmd of
И Haskell не смог оптимизировать. Когда все слил в один - скорость вернулась!!!
no subject
Date: 2016-09-29 10:36 am (UTC)Я не помню контекста моего совета, к сожалению.
no subject
Date: 2016-09-29 11:04 am (UTC)новый
старый
Совет был - так будет более в стиле haskell.
no subject
Date: 2016-09-29 01:07 pm (UTC)Так что получается при увеличении размера первого поколения кучи?
no subject
Date: 2016-09-29 01:18 pm (UTC)cmdType "PING" = Just CmdToAll
Я думал, что строки тут, как и тут
l = [("PING", 1), ...]
"готовые"....
А почему они не готовые в первом случае?
no subject
Date: 2016-09-29 01:45 pm (UTC)А то я что-то упускаю, наверняка. А внимательно смотреть у меня нет сил. :(
no subject
Date: 2016-09-29 02:30 pm (UTC)cmd_type = [("PING", 1), ...]
case lookup k cmd_type of Just v -> ...
Второй вариант:
data CmdType = CmdToAll | ...
cmdType "PING" = Just CmdToAll
case cmdType k of Just v -> ....
Только что сделал простые бенчмарки этих вариантов.
Ведут себя одинаково, второй даже быстрей, как и думал.
Загвоздка в том, что в приложении, а не в тесте, скорости другие.
Ну, да ладно....
no subject
Date: 2016-09-29 04:24 pm (UTC)Я подозреваю, что часть исходной строки просто переносится в выход каким-то образом в первом, а во втором варианте выход создаётся "из чистого воздуха", приводя новой работе CPU по созданию и убиранию данных.
Причина загвоздки как раз в разнице в оставшейся части программы.
no subject
Date: 2016-09-29 05:07 pm (UTC)Вся разница - объявление типа и изменение case для выбора логики работы:
И все.
no subject
Date: 2016-09-29 05:12 pm (UTC)Например, определение команд.
Его же на гитхабе нет.
no subject
Date: 2016-09-30 03:24 am (UTC)PING посылается на все сервера-ноды.
SET k v - на ноду, которую вычислили по ключу k
Есть догадка!
А может когда пишешь функцию
cmdType "PING" = Just CmdToAll
, то не смотря на {-# LANGUAGE OverloadedStrings #-}
тут "PING" - это обычные строки, а не ByteString.
Тогда все становиться на свои места: большое значение MUT.
no subject
Date: 2016-09-30 11:14 am (UTC)Я склоняюсь к тому, что команда пересоздаётся.
no subject
Date: 2016-09-30 12:06 pm (UTC)Изменил ответ cmdType с
cmdType :: ByteString -> Maybe CmdType
на
cmdType :: ByteString -> Maybe Integer
И скорость вернулась на свои места.
Получается из-за типа данных.
data CmdType = CmdToAll | CmdToOne | CmdToMany | CmdToManyValues | CmdToManyTimeout
Но почему же?
no subject
Date: 2016-10-03 06:17 am (UTC)И Haskell не смог оптимизировать. Косда все слил в один - скорость вернулась!!!