[personal profile] iamjaph
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] thesz посоветовал заменить это на дата-тип.

Заменил на:
 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 не смог оптимизировать. Когда все слил в один - скорость вернулась!!!

Date: 2016-09-29 10:36 am (UTC)
From: [identity profile] thesz.livejournal.com
Что делает увеличение объёма первого поколения кучи? Это раз - поскольку замедление может быть из-за выделения памяти.

Я не помню контекста моего совета, к сожалению.

Date: 2016-09-29 11:04 am (UTC)
From: [identity profile] iamjaph.livejournal.com
Разница в MUT сильная.

новый
     926,698,696 bytes allocated in the heap
       6,363,908 bytes copied during GC
          45,916 bytes maximum residency (2 sample(s))
         109,804 bytes maximum slop
              19 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0        69 colls,     0 par    0.034s   0.035s     0.0005s    0.0014s
  Gen  1         2 colls,     0 par    0.000s   0.001s     0.0004s    0.0004s

  TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)

  SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    1.118s  ( 16.579s elapsed)
  GC      time    0.034s  (  0.036s elapsed)
  EXIT    time    0.001s  (  0.002s elapsed)
  Total   time    1.153s  ( 16.617s elapsed)

  Alloc rate    829,256,125 bytes per MUT second

  Productivity  97.0% of total user, 6.7% of total elapsed


старый
     808,138,168 bytes allocated in the heap
       4,926,496 bytes copied during GC
          46,292 bytes maximum residency (3 sample(s))
         103,032 bytes maximum slop
              21 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0        57 colls,     0 par    0.025s   0.026s     0.0005s    0.0013s
  Gen  1         3 colls,     0 par    0.001s   0.001s     0.0003s    0.0005s

  TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)

  SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    0.939s  (  4.114s elapsed)
  GC      time    0.026s  (  0.027s elapsed)
  EXIT    time    0.000s  (  0.005s elapsed)
  Total   time    0.964s  (  4.147s elapsed)

  Alloc rate    860,990,959 bytes per MUT second

  Productivity  97.4% of total user, 22.6% of total elapsed


Совет был - так будет более в стиле haskell.
Edited Date: 2016-09-29 11:05 am (UTC)

Date: 2016-09-29 01:07 pm (UTC)
From: [identity profile] thesz.livejournal.com
В новом коде создаются строки, которые в старом коде (как я понимаю) не создавались, а использовались готовые.

Так что получается при увеличении размера первого поколения кучи?

Date: 2016-09-29 01:18 pm (UTC)
From: [identity profile] iamjaph.livejournal.com
Как тут не готовые строки?

cmdType "PING" = Just CmdToAll

Я думал, что строки тут, как и тут

l = [("PING", 1), ...]

"готовые"....
А почему они не готовые в первом случае?



Date: 2016-09-29 01:45 pm (UTC)
From: [identity profile] thesz.livejournal.com
Право слово, я должен попросить объяснить мне дизайн первого и нового варианта.

А то я что-то упускаю, наверняка. А внимательно смотреть у меня нет сил. :(

Date: 2016-09-29 02:30 pm (UTC)
From: [identity profile] iamjaph.livejournal.com
Первый вариант:

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 -> ....

Только что сделал простые бенчмарки этих вариантов.
Ведут себя одинаково, второй даже быстрей, как и думал.

Загвоздка в том, что в приложении, а не в тесте, скорости другие.

Ну, да ладно....

Date: 2016-09-29 04:24 pm (UTC)
From: [identity profile] thesz.livejournal.com
А что дальше происходит с данными в первом и втором вариантах?

Я подозреваю, что часть исходной строки просто переносится в выход каким-то образом в первом, а во втором варианте выход создаётся "из чистого воздуха", приводя новой работе CPU по созданию и убиранию данных.

Причина загвоздки как раз в разнице в оставшейся части программы.

Date: 2016-09-29 05:07 pm (UTC)
From: [identity profile] iamjaph.livejournal.com
А другой разницы нет.
Вся разница - объявление типа и изменение case для выбора логики работы:
-   case lookup cmd cmd_type of
-           Just 1 -> do -- На все сервера
+   case cmdType cmd of
+           Just CmdToAll -> do -- На все сервера
....
-           Just 2 -> do -- На конкретные сервер
+           Just CmdToOne -> do -- На конкретные сервер
....

И все.
Edited Date: 2016-09-29 05:08 pm (UTC)

Date: 2016-09-29 05:12 pm (UTC)
From: [identity profile] thesz.livejournal.com
Дайте же мне побольше контекста.

Например, определение команд.

Его же на гитхабе нет.

Date: 2016-09-30 03:24 am (UTC)
From: [identity profile] iamjaph.livejournal.com
Тут команда просто строка из протокола http://redis.io
PING посылается на все сервера-ноды.
SET k v - на ноду, которую вычислили по ключу k

Есть догадка!
А может когда пишешь функцию
cmdType "PING" = Just CmdToAll
, то не смотря на {-# LANGUAGE OverloadedStrings #-}
тут "PING" - это обычные строки, а не ByteString.
Тогда все становиться на свои места: большое значение MUT.

Date: 2016-09-30 11:14 am (UTC)
From: [identity profile] thesz.livejournal.com
Догадка, скорее, неправильная.

Я склоняюсь к тому, что команда пересоздаётся.

Date: 2016-09-30 12:06 pm (UTC)
From: [identity profile] iamjaph.livejournal.com
Нашел.
Изменил ответ cmdType с
cmdType :: ByteString -> Maybe CmdType
на
cmdType :: ByteString -> Maybe Integer
И скорость вернулась на свои места.

Получается из-за типа данных.
data CmdType = CmdToAll | CmdToOne | CmdToMany | CmdToManyValues | CmdToManyTimeout

Но почему же?

Date: 2016-10-03 06:17 am (UTC)
From: [identity profile] iamjaph.livejournal.com
Загвоздка была, в том, что cmdType, так как много строк, поместил в другой файл, а не в тот, где case cmdType cmd of

И Haskell не смог оптимизировать. Косда все слил в один - скорость вернулась!!!

Profile

iamjaph

March 2025

S M T W T F S
      1
2345678
9101112131415
16171819 202122
23242526272829
3031     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 11th, 2025 03:14 pm
Powered by Dreamwidth Studios