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)