LINUX.ORG.RU

XPending CPU overload

 ,


0

1

XPending в цикле-обработчике событий while кушает 99% процессора. Как это лечить? Точнее, как это сделать правильно по инструкции. Ибо половина примеров в сети не используют XPending, половина используют, в оф. документации скромно в две строки написано о том, что она возвращает количество текущих не распределенных сообщений.

while (!terminated)
{
   if (XPending(d))
   {
       XNextEvent(...)
       ...
   }
}


Я сделал что-то такое для отрисовки opengl сцены и проца оно не жрет:

{
    XEvent event;
    KeySym key;

    double rotQuad, rotTri = 0;

    while (!mScene->stopSceneFlag)
    {
        /* handle the events in the queue */
        while (XPending(pDisplay) > 0)
        {
            XNextEvent(pDisplay, &event);
            switch (event.type)
            {
                case ConfigureNotify:
                {
                    std::pair<int,int> curDimensions = getWindowDimensions();
                    if ((event.xconfigure.width != curDimensions.first) ||
                        (event.xconfigure.height != curDimensions.second))
                    {
                        setWindowDimensions(event.xconfigure.width, event.xconfigure.height);
                        mScene->resizeGLScene(event.xconfigure.width,
                            event.xconfigure.height);
                    }
                    break;
                }
                /* exit in case of a mouse button press */
                case ButtonPress:
                {
                    mScene->stopScene();
                    break;
                }
                case KeyPress:
                {
                    key = XLookupKeysym(&event.xkey, 0);
                    keyPressed(key);
                    break;
                }
                case ClientMessage:
                {
                    if (*XGetAtomName(pDisplay, event.xclient.message_type) ==
                        *"WM_PROTOCOLS")
                    {
                        mScene->stopScene();
                    }
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        mScene->drawGLScene(&rotTri, &rotQuad);
    }
    killWindow();
}

anonymous
()

Если у тебя именно такой цикл, то 99% жрет не сам XPending(), а прокрутка впустую, когда эта функция ничего не возвращает путного. Добавь else-ветку, поспи там чуть-чуть перед следующей итерацией.

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

Спать нельзя, ибо пока буду спать может прийти штук 20 моих кастомных сообщений. А может и не прийти.

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

А их нельзя обработать последовательно? Ну пришло их 20 штук, ты свой цикл на 20 итераций занял делом. Там же наверняка очередь из сообщений строится.

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

Странно. Я ставил бряку на XNextEvent в своем проекте. Сообщения мне не приходят (не подписывался), оверлоад есть.

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

Очередь строят иксы. Мне принципиально важно обработать сообщение через минимальный промежуток времени после его прихода (например установлен таймер, через каждые 20 мс посылающий кастом сигнал. я на него подписываюсь, меняю содержимое image.data и посылаю сигнал перерисовки. Прошляплю пару сигналов таймера - потеряю кадры).

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

Ну тогда в идеале тебе нужен блокирующий вызов а-ля XPending. Или средство синхронизации типа мьютекса. Может, стоит таймер переделать, чтобы не события слал, а мьютекс передергивал? Тогда цикл у тебя будет стопориться, а не крутиться, пожирая процессор.

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

Спасибо, пока не нужно. У меня оно работает так:

int SmlMain()
{
    XEvent event;
 
    SmlEventStruct data;
 
    while (warehouse.destroyflag != SML_DEAD)
    {
        if (XPending(warehouse.display))
        {
            XNextEvent(warehouse.display, &event);
 
            nulldata(&data);
 
            data.window = findWindow(event.xany.window);
            if (data.window == SML_CANTFIND)
            {
                return SML_ERR_BADWINDOW;
            }
 
            switch (event.type)
            {
            // Тут обработчики событий, скипнул, ибо простыня
            default:
                break;
            }
        }
    }
 
    return SML_ERR_SUCCES;
}
Ставлю бряку на XNextEvent, вижу, что сообщения не приходят. Загрузка проца при это в 99%. Есть разница между while(xpending) и if (xpending)?

inn
() автор топика

Вообще для подобных задач в сетевом стеке (BSD sockets API) есть вариант select с таймаутом. Задаешь ненулевой таймаут и будет как раз таки sleep в простое (если новых данных нет) на заданное время. Но как только придут данные sleep прервется (не дожидаясь завершения таймаута) и select вернет, что есть данные.

Единственный минус - очень частая но невысокая нагрузка может увеличить пожирание проца до 100%. Например, если тебе пришло по 1 байту несколько сотен раз в секунду у тебя загрузка будет высокой (~100%). Можно было бы подождать несколько миллисекунд и обработать весь кусок целиком. Например, вместо того чтобы 1000 раз в секунду анализировать каждый байт, можно подождать 100мс и всего 10 раз за секунду «промесить» по 100 байт (ну или сколько там реально наберется). При сетевом обмене вариант с жестким sleep обычно дает меньшую нагрузку на процессор чем select с таймаутом (хотя бы за счет того, что количество вызовов функций будет гарантировано меньшим). 100% загрузка также может быть объяснена некоторым минимальным квантом, который наверняка есть в линуксе при учете процессорного времени.

Для X11 ищи аналог XPending по типу select'а, раз стоит задача быстрой реакции на события. Или попробуй какой-то событийный вариант с callback'ами (но врядли это есть в libX11 и скорее всего программа изменится до неузнаваемости).

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

Ок, подведу итог той информации, которая есть сейчас. Я пишу проект, в котором используются Xlib-функции. В нем есть главный цикл сбора сообщений (ГЦСС). Выглядит он так: XPending CPU overload (комментарий) При таком виде (даже если поменять if(xpending) на while(xpending) он отжирает 100% процессора. Я подумал, что это у меня проблема с моим проектом. Я запустил тот код, что дал мне beastie здесь: Как корректно завершить xlib программу? (комментарий) ОН ТОЖЕ кушает 100%. Я открыл код GTK+, место где у них цикл сбора иксовых сообщений (где он находится, точно сейчас не скажу, смотрел с работы), там все идентично, но что-то я не заметил, чтобы GTK приложения отжирали 100%.

Закомментил XPending (т.е. его роль стал выполнять XNextevent, ибо он тоже блокирующий до следующего сообщения (проверено отладчиком)). И, о, чудо! Теперь я приложение даже в top'е не вижу, жрет меньше десятой процента, но...

..но теперь Custom сообщения не хотят доходить. Точнее они приходят, но будучи сгенеренными за 2.5 секунды (5 штук), обрабатываются только когда что-то «родное» будет вызвано, типа KeyPressed и т.д.

Собственно, вопрос: как вообще by design должно это работать и не жрать 100%?

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

Решил. Поменял местами код:

while (warehouse.destroyflag != SML_DEAD)
{
    while (XNextEvent(warehouse.display, &event) >= 0)
    {

Функция треда, который шлет сообщения CustomMessage (добавил flush)

XLockDisplay(warehouse.display);
{
     XSendEvent(warehouse.display,
                event.xclient.window,
                0, NoEventMask, &event);
     XFlush(warehouse.display);
}
XUnlockDisplay(warehouse.display);
inn
() автор топика
Ответ на: комментарий от inn

Варианта видимо 2: либо явно «подписаться» на нужные Cystom Events (чтобы их видел XNextevent, потому как он должен реагировать на все подряд по-идее), либо юзать универсальный костыль в виде кратковременного sleep'а в некоем основном цикле.

Вот еще костыль по теме http://www.linuxquestions.org/questions/showthread.php?p=2431345#post2431345 , но там залезли очень глубоко вовнутрь, имхо должен существовать более простой вариант...

На libX11 я даже HelloWorld еле-еле осилил, так что ничего серьезного про него не скажу...

anonymous
()
Ответ на: комментарий от inn

Функция треда, который шлет сообщения CustomMessage (добавил flush)

ОК, ну и отлично... Костыли получается не нужны.

anonymous
()

Я делал на XCB с sleep (ну точнее я там юзал Qtшные таймеры). Там можно прочитать все сообщения которые накопились за всё время. Думаю в Xlib тоже так можно.

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

Я делал на XCB с sleep (ну точнее я там юзал Qtшные таймеры). Там можно прочитать все сообщения которые накопились за всё время. Думаю в Xlib тоже так можно.

Да, в принципе, если проблема только в том, чтобы не пропустить сообщение, то проблем нет, в варианте со sleep ничего пропущено не будет. Если задача именно в обеспечении минимального отклика, то лучше вариант с XNextEvent. Можно еще повозить окно по экрану и другие приложения над окном (с выключенного XComposite), или поресайзить окно в каком-нибудь wm без кеширования перерисовок (например, в fvwm или twm). Если загрузка приемлемая, то лучше оставить XNextEvent. Если нереально высокая и все равно возникают задержки, мерцания и лаги, то лучше оставить sleep и попробовать сделать оптимизацию перерисовок (т.е. поспать, накопить n штук перерисовок, когда события кончатся, то сделать 1 перерисовку в конце, не важно сколько накопилось, а потом уже спать дальше).

В принципе, обычно не нужен прям максимально быстрый отклик, поэтому sleep и без заморочек. Чтобы достичь плавности можно каждый раз перед перерисовкой брать системное время или clock и адаптировать сцену к нужному времени (фреймворки для анимаций обычно делают примерно так). А в играх вообще циклы без sleep ставят, там обычно glFinish() и swapBuffers делают задержку. Если выключить двойную буферизацию, то проц тоже на 100% будет загружен (и даже считается, что это хорошо :) )...

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

Если слип на одну миллисекунду, а то и меньше то вообще понижения отклика не заметишь практически) usleep(1) там какой-нибудь.
и код примерно такой:
таймер

_timer.setInterval(1);
connect(&_timer, SIGNAL(timeout()), _taskbarLogic, SLOT(ProcessEvents()));
_timer.start();
логика
void TaskbarLogic::ProcessEvents()
{
    xcb_generic_event_t *event;
    xcb_property_notify_event_t *propertyNotify;

    while( (event = xcb_poll_for_event(_xcbHandler->Connection())) )
    {
        switch(event->response_type)
        {
            case XCB_PROPERTY_NOTIFY:
            {

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

А по поводу игр. Если отключить вертикальную синхронизацию будет 100% cpu. Двойная буферизация то немного другое. Без неё вообще лажа будет на экране (мерцания анимированных спрайтов и другое)

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

В общем случае невозможно выбрать «правильный» или «самый оптимальный» вариант. Любой из рассмотренных вариантов может быть оптимальным в определенной ситуации, но «требования» и «ситуации» могут быть совсем разными. Sleep уже предлагали, но типа не подходит (хотя даже 10мс - это 100 раз в секунду, более чем достаточно для вывода кадра, даже дрифт еще на 10 мс - это 50 раз в секунду).

Если вдруг сейчас выяснится, что XNextEvents без наворотов при ресайзе даст лаг в несколько секунд (в glxgears такое иногда наблюдается). И если вдруг такая ситуация окажется неприемлемой ни в коем случае, то придется какой-то другой вариант выбирать... Возможно sleep окажется более простым и дешевым.

anonymous
()

проблемы нет. XPending() проверил и вернулся, цикл читай пустой. даже если добавить рендер.

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