Re: fprintf && write

From
Boris Rudakov (2:5054/9.4)
To
Andrey Melnikov ()
Date
2003-06-04T10:57:06Z
Area
RU.UNIX.PROG
Hello Andrey!

31 май 03 21:16, you wrote to All:

 AM>                                Hello All!

 AM> Что-то у меня лыжи не едут совсем.

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

Часть замечаний совсем "общего плана", не касаемо заданного вопроса.

 AM> Беру софтину, которая всю свою разумную жизнь работала так:

 AM>   out = fdopen(out_fd, "w");
 AM>   ....

 AM>   fprintf(out, "%s", buffer);

Не вижу ни малейшего смысла в подобных конструкциях. Кишки xrintfxxx заняты тем, что интерпретируют единственный "%s" и тупо перекачивают buffer в out. Накладные расходы в этой и подобных конструкциях несущественны, но... Зачем они когда можно и без них ?

Я в таких случаях пишу данные непосредственно туду куда нужно, избегая лишних переписываний во внутренних буфферах.

Чен'ть типа write(out, .....);

 AM>   ....

 AM> Выкидываю все эти fprintf() и заменяю на свою функцию:

 AM> static char wr_buf[1024];

Для теста - прокатит, а вообще стоит избегать глобальных переменных. Я нахожу что весь код нужно писать реентерабельным. Если нет ОСОБЫХ причин делать иначе :) Я бы этот буфер увеличил раза в 4 и засунул внутрь функции :)

 AM> write_out (int fd, char *fmt, ...){
 AM>     va_list ap;
 AM>     fd_set wfds;
 AM>     struct timeval tv;
 AM>     int wlen = 0, len, wr;

 AM>     va_start(ap,fmt);
 AM>     (void) vsnprintf(wr_buf,sizeof(wr_buf),fmt,ap);
 AM>     va_end(ap);

 AM>     FD_ZERO (&wfds);
 AM>     FD_SET (fd, &wfds);
 AM>     len = strlen(wr_buf);
Это излишне, я бы запоминал длину сразу же при вызове vsnprintf

 AM>     while (wlen != len){
А тут просто ошибки. Следовало бы сделать так (см. это и ниже):
         whle (len > 0) {

 AM>           tv.tv_sec = timeout; /* timeout; */
 AM>           tv.tv_usec = 0;

 AM>           if (!select (fd + 1, NULL, &wfds, NULL, &tv)){
 AM>              return -1;
 AM>           }
Вот по этому поводу знающие unix API уже сказали тебе что это основная точка тормозов. Скорее это так, но и без этого нужно иметь в виду следующее:
1. Любой вызов ядра - относительно дорог и их нужно делать обдуманно-редко. Даже алгоритм синхронизации нитей нужно проектировать так, чтобы код обращался к объектам синхронизации как можно реже. Код ввода-вывода, если нет особых требований делать обратное, должен вызывать функции IO api как можно реже.

 AM>         wr = write(fd,wr_buf,len);

 AM>         if (wr < 0){
 AM>            return -1;
 AM>         } else {
 AM>            wlen += wr;
И вот тут ошибка. Следовало бы так:
                len -= wr;
                wr_buf += wr;
 AM>         }
 AM>     }

 AM>    return 0;
 AM> }

 AM> И скорость падает в 2 (два) с лишним раза. Где я тут неправ ?

Если рассудить логически, то:

1. write - синхронный блокирующий вызов (или я не прав ?). Но, как бы то ни было, ты скармливаешь ему весь буффер. Если бы он у тебя выводил не весь буффер, а часть - ты бы давно уже заметил что твой код неправилен и поправил бы приращение буфера и декремент длины. Значит - write у тебя пишет все одним махом. Значит - глюк не в нем.

2. Кроме write у тебя есть только один вызов API - select (в Виндах я бы сказал "Вызов функции ядра", но думаю что и тут с select этот термин будет точен). Морал - тормозит (сверх ожидаемого) он.

Моя теория такова (еще раз шаркну ножкой что я не юниксоид, но основные принципы едины):

* Твой код делает два системных вызова.

* Твой код выводит все за одну итерацию (иначе ты бы уже заметил ошибки в нем). Итого - ты делаешь ровно один select и ровно один write.

* "Меня терзают смутные сомнения" что fprintf же делает только один - синхроный блокирующий write. И делает только один write (правда х.з. что он делает при переполнении внутренних буфферов при форматировании строки - может быть он делает серию write по мере их заполнения; однако, думаю что в твоем случае ему там хватает одного write).

* Так же меня "терзают смутные сомнения" что select - дорогой вызов, дороже write.

Таким образом, сдается мне что двукратное замедление вызвано тем, что ты в два раза чаще беспокоишь ядро системы :)

 AM>      Andrey aka TEMHOTA-RIPN

Борис Рудаков,               Лучше больно, чем непонятно...
BBR

--- Be happy: BBR is looking at you !
 * Origin: АлкАголь малыми дозами безвреден в любых количествах (2:5054/9.4)