mmap хм...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

mmap хм...

Anton Maksimenkov
Hi.

 Опять из области теоретических изысков. Вопрос совершенно не "горит",
исключительно для тренировки мозга и постижения нового. Хотя если он
откроет более эффективный путь, я пожалуй перепишу кой-чего из своего
барахлишка, ибо назрело время оптимизации.

  Начитывая 4.4BSD D&I нашёл рассуждения типа "обычно при системном
вызове данные копируются между пользовательским пространством и
пространством ядра" и что "иногда это может быть значительной
нагрузкой" при большом количестве данных. И вот-де был задуман mmap(),
чтобы обмен большими объемами данных сделать менее пожирательным к
ресурсам.

 "Я решил попробовать, бутылку взял открыл" (с). Вышло... ннне очень.
 
 Ситуация такова. Есть фрагмент, в котором используется read() - это
тот обработчик логов, который я приводил при обсуждении write -> read,
при чтении увеличивающегося файла. Он к тому же делает bcopy()
недочитанной части строки из конца буфера в начало (чтобы при
следующем read() дочитывать её), т.е. доп. работу делает. Вот рабочая
петля так сказать (привожу лишь для примера):
---------------------------------------------------------------------
        for(;;) {
                nr = 0;
                while (nr == 0) {
                        /* LINEBUF_SIZE = 131072 */
                        if ((nr = read(inf, curpos, LINEBUF_SIZE - 1 - (curpos - linebuf))) == -1) {
                                syslog(LOG_ERR, "read(): %s", strerror(errno));
                                exit(errno);
                        }
                        if (nr == 0) {
                                if ((nev = kevent(kq, kev, 1, kev, 1, NULL)) < 0) {
                                        syslog(LOG_ERR, "kevent(): %s", strerror(errno));
                                        exit(1);
                                }
                        }
                }
                *(curpos + nr) = '\0';

                strpos = linebuf;
                if ((p = strchr(strpos, '\n')) == NULL) {
                        f_hugeline = 1;
                        curpos = linebuf;
                        printf("it IS huge line - will NOT processed\n");
                        syslog(LOG_INFO, "HUGE: %s", strpos);
                } else {
                        if (f_hugeline == 1) {
                                *p = '\0';
                                strpos = p + 1;
                                f_hugeline = 0;
                                printf("it WAS huge line - part of huge line will NOT processed\n");
                        }
                        while ((p = strchr(strpos, '\n')) != NULL) {
                                *p = '\0';
                                if (process(strpos, &logdata, &timers) == -1) {
                                        syslog(LOG_ERR, "process() failed");
                                        exit(errno);
                                }
                                strpos = p + 1;
                        }
                        if (strpos != curpos + nr) {
                                /*printf("part of string uncompleted - will be completed after read() and then processed\n");*/
                                bcopy(strpos, linebuf, nr - (strpos - curpos));
                                curpos = linebuf + (nr - (strpos - curpos));
                        } else {
                                curpos = linebuf;
                        }
                }
        }
---------------------------------------------------------------------

 Я подумал - вот у меня как раз ситуация, когда обрабатывается большой
файл и много данных перекачивается. Лог пополняется обильно, так что
это верно и тогда, когда процесс дошёл до конца файла - он получает
данные со скоростью нескольких десятков строк в секунду.
 Одним словом, указывает на то, что вроде как mmap() для этого более
подходит. Ну и сделал тест. Приведенную выше петлю переделал на
mmap(), которая большими (5242880) кусками хватает файл, читает,
munmap()'ит; хватает следующий кусок файла...
 НАМЕРЕННО НЕ ОБРАЩАЛ ВНИМАНИЕ НА ОШИБКИ, ИСКАЖЕНИЯ и возможные вылеты
за пределы зоны, ХОТЕЛ ПРОСТО СРАВНИТЬ СКОРОСТЬ. Вот кусок:
---------------------------------------------------------------------
        if (fstat(inf, &sb) == -1) {
                syslog(LOG_ERR, "fstat(): %s", strerror(errno));
                exit(1);
        }
       
        if (sb.st_size / 10 > 5242880)
                nr = 5242880;
        else
                nr = sb.st_size / 10;
        for (ch = 0; ch < sb.st_size; ch += nr) {
                if ((mptr = mmap(NULL, nr, PROT_READ | PROT_WRITE, MAP_PRIVATE, inf, ch)) == MAP_FAILED) {
                        syslog(LOG_ERR, "mmap(): %s", strerror(errno));
                        exit(1);
                }
                strpos = mptr;

                /* Здесь дурацкий workaround - чтобы сразу не вылетать
                за границу участка во время strchr() */
                if (ch + nr < sb.st_size) {
                        *(mptr + nr - 2) = '\n';
                        *(mptr + nr - 1) = '\0';
                } else {
                        *(mptr + sb.st_size - 2) = '\n';
                        *(mptr + sb.st_size - 1) = '\0';
                }

                if ((p = strchr(strpos, '\n')) == NULL) {
                        f_hugeline = 1;
                        curpos = mptr;
                        printf("it IS huge line - will NOT processed\n");
                        syslog(LOG_INFO, "HUGE: %s", strpos);
                } else {
                        if (f_hugeline == 1) {
                                *p = '\0';
                                strpos = p + 1;
                                f_hugeline = 0;
                                printf("it WAS huge line - part of huge line will NOT processed\n");
                        }
                        while ((p = strchr(strpos, '\n')) != NULL) {
                                *p = '\0';
                                if (process(strpos, &logdata, &timers) == -1) {
                                        syslog(LOG_ERR, "process() failed");
                                        exit(errno);
                                }
                                strpos = p + 1;
                        }
                }

                if (munmap(mptr, nr) == -1) {
                        syslog(LOG_ERR, "munmap(): %s", strerror(errno));
                        exit(1);
                }
        }
---------------------------------------------------------------------

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

 И что же получилось? Взял тестовый файл, 720 000 строк. Первая
версия, использующая read(), при запуске c time, обрабатывает его
примерно за 7 секунд, без излишней нагрузки.

$ time ./aclogd -f ../log
    0m6.48s real     0m1.85s user     0m4.04s system

 Однако вторая версия, использующая mmap(), работает медленно,
судя по top процессор проводит более 90% в system (против 60% у 1-й),
заканчивается примерно через 25 сек

$ time ./aclog2d -f ../log
Segmentation fault
    0m25.40s real     0m0.81s user     0m23.33s system

   
 То есть более чем в 3 раза медленнее. И то не уверен, что она
обрабатывает весь файл (но искать баг совершенно незачем было).
И сижу, знач, чешу репу в поисках утраченного перформанса: толи это я
чо не так понял, толи схему использования mmap() неправильно применил.
Есть у кого выражения на эту тему?

--
engineer


Reply | Threaded
Open this post in threaded view
|

Re: mmap хм...

Alexander Yurchenko
On Sat, Jan 07, 2006 at 10:59:03PM +0500, Anton Maksimenkov wrote:
> чо не так понял, толи схему использования mmap() неправильно применил.
> Есть у кого выражения на эту тему?

Имелись ввиду следующие схемы:

read(buf)
write(buf)

buf = mmap
write(buf)

А в вашем случае процесс занимается исключительно обработкой page
fault'ов.

>
> --
> engineer
>

--
   Alexander Yurchenko