LINUX.ORG.RU

[lisp, sbcl, streams, галлюцинации] Тормоз в write-sequence


0

0

Есть большой-большой, размером 512 млн, vector c элементами типа '(unsigned-byte 8). Нужно его отправить в сокет, делаю так:

(write-sequence bullet (usocket:socket-stream sock))
Но! тут начинаются галлюцинации: процесс sbcl съедает всё процесорное время, принимающий процесс блокируется на select()-е (неважно какой таймаут), через минут 10 программа отвисает, цпу расслабляется, вектор отправлен.

При повторной отправки этого же вектора галлюцинации снова не появляются.

Кто-нибудь с таким сталкивался? Как решил? ЧЯДНТ?


Не знаю, не использовал, но похоже на создание буфера при первом вызове, а потом уже, при повторном, его обычное использование. Возможно, что буфер большой создается. А теперь это надо проверить. :)

Zubok ★★★★★
()

Вы, видимо, сразу целиком запихиваете все. Попробуйте отправлять кусками в цикле.

Apkawa
()

Запусти sbcl с твоей софтиной через /usr/bin/time -v. Результаты сюда. Скорее всего, page faults буде зашкаливать из-за маппинга старничек в адресное пространство.

mv ★★★★★
()
Ответ на: комментарий от mv

2Apkawa, если по кускам отправлять, та же бойда.
2mv, оть:
Wed Aug 26 16:20:46 2009: DATA: sending 483800000 bytes.
Wed Aug 26 16:35:52 2009: DATA: sent.
Wed Aug 26 16:36:07 2009: DATA: sending 483800000 bytes.
Wed Aug 26 16:36:29 2009: DATA: sent.
Wed Aug 26 16:36:43 2009: DATA: sending 483800000 bytes.
Wed Aug 26 16:37:06 2009: DATA: sent.
        Command being timed: "./start.lisp --daemon"
        User time (seconds): 6.39
        System time (seconds): 0.40
        Percent of CPU this job got: 0%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 1:22:12
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 0
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 72355
        Voluntary context switches: 4
        Involuntary context switches: 11
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0

Как бороться? (:

l5k
() автор топика
Ответ на: комментарий от l5k

Сделал небольшой сишный вариант: сервер, и клиент отсылающий серверу 483800000 bzero()-нутых байтов:
Код получения:
while ((read_bytes = recv(sd_client, buffer, sizeof(buffer), 0)) != 0)
    if (read_bytes == -1)
        exit(1);

Замечено что при
char buffer[1024 * 1024];
весь массив пересылается за ~4 c. Minor faults: 140

При
char *buffer = (char *)malloc(1024 * 1024);
массив пересылается за ~30 с. Minor failts: 397

l5k
() автор топика
Ответ на: комментарий от l5k

Про сишный вариант отбой, я про sizeof(char *) забыл. %))
Оба варианта работают по 4 с.

l5k
() автор топика
Ответ на: комментарий от mv

Хочется мне большую кучу объектов (их бинарное представление обеспечивается с помощью binary-types) перед неоднократной отправкой в сокет закешировать в массиве. Этот массив и применяется к write-sequence.

Глянул в http://repo.or.cz/w/sbcl.git?a=blob;f=src/code/stream.lisp;h=4158071831a2ba00a1a96a14677ab2328bcf98d2;hb=HEAD и увидел свет:
;; This might be a bivalent stream, so we need
;; to dispatch on a per-element basis, rather
;; than just based on the sequence or stream
;; element types.
Если я правильно понял, то write-sequence работает поэлементно, ололо.

В общем, попробую с teepeedee2 стянуть byte-vector и выбросить usocket. На прототипе при перессылки 400 Мб тормозов не наблюдал, вроде бы обнадёживает. (:

l5k
() автор топика
Ответ на: комментарий от l5k

> Если я правильно понял, то write-sequence работает поэлементно, ололо.

dispatch элементов в SBCL и передача однобайтного буфера в ядро - это разные вещи. Ты, скорее всего, просто отхапал вагон памяти, потом пытаешься записать его куда-то. Ядро, естественно, память тебе выделила, но лениво, т.к. pagemap не сделало. Делает по первому обращению, ловя general protection fault, что есть тормоз. Соответственно, если пробежаться по всей памяти заранее, то запись должна отработать быстро.

mv ★★★★★
()
Ответ на: комментарий от mv

> если пробежаться по всей памяти заранее
Я же этот кусок памяти сам строю, т.е. как минимум я в него один раз уже писал.

l5k
() автор топика
Ответ на: комментарий от l5k

> Я же этот кусок памяти сам строю, т.е. как минимум я в него один раз уже писал.

OK. А посмотри ещё в top, в момент отправки ЦПУ напрягается в юзерспейсе или в ядре?

mv ★★★★★
()
Ответ на: комментарий от mv

От так оно у меня выглядит:
Cpu(s):  0.2%us,  1.3%sy,  0.0%ni, 98.1%id,  0.0%wa,  0.0%hi,  0.4%si,  0.0%st
Mem:  16470072k total,  9453356k used,  7016716k free,    61888k buffers
Swap:  2096440k total,        0k used,  2096440k free,  5416520k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                    
 3538 root      20   0 16.1g 3.3g  22m S 29.2 21.2  24:49.70 sbcl                                        

Насколько я понимаю, не в юзерспейсе.

l5k
() автор топика
Ответ на: комментарий от l5k

У тебя процессор 98% времени спит.

mv ★★★★★
()
Ответ на: комментарий от l5k

http://paste.lisp.org/+1UGJ

В коде есть две функции: it-is-slow, it-is-fast - одна тормозит (~93 c), другая шустрит (~2 с). Если *crap-length* дать значение (* 100 1024 1024), т.е. в 2 раза меньшее, то it-is-slow начинает непропорционально шустрить - 8 с.

Вот более точный вывод top (с предыдущим я ступил/поленился и дал какой-то левый):
top - 19:51:25 up 1 day,  6:46,  3 users,  load average: 0.28, 0.08, 0.02
Tasks: 206 total,   1 running, 205 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.0%us,  1.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
...
Cpu5  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu6  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
...
Cpu15 :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  16470072k total,  3191560k used, 13278512k free,    57184k buffers
Swap:  2096440k total,        0k used,  2096440k free,  2185776k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                    
 4873 root      20   0 8453m 609m  23m S 99.6  3.8   0:23.84 sbcl --load write-seq.lisp --eval (write-seq
 4793 root      20   0 14880 1244  888 R  1.0  0.0   1:26.08 top                                         

После долгих гаданий, выяснилось, что всё дело в этой строчке:
(setf (sb-bsd-sockets:non-blocking-mode socket) t)
Если её закомментить или заюзать
(setf (sb-bsd-sockets:sockopt-tcp-nodelay socket) t)
то тормозов не наблюдается. Разве они не эквивалентны?

l5k
() автор топика
Ответ на: комментарий от l5k

Ага! Почитал я про non blocking и nodelay. Раньше думал, что они оба отключают алгоритм Нагла и делают сокет неблокирующим, где-то я такое вычитал... %)
Осталось понять почему неблокирующий сокет так сказывается на write-sequence...

l5k
() автор топика
Ответ на: комментарий от l5k

> (setf (sb-bsd-sockets:non-blocking-mode socket) t)
> Если её закомментить или заюзать

> (setf (sb-bsd-sockets:sockopt-tcp-nodelay socket) t)

> то тормозов не наблюдается. Разве они не эквивалентны?


Нет, конечно!

mv ★★★★★
()
Ответ на: комментарий от l5k

> Осталось понять почему неблокирующий сокет так сказывается на write-sequence...

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

mv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.