• Авторизация


О скорости рисования 29-06-2013 01:30


Обнаружил сегодня интересную особенность рисования символов на экране. По работе пишу софтину, показывающую попакетно сетевые дампы (специально под пакеты определённого формата). Столкнулся с неприятной особенностью: при отображении большого количества текста отрисовка начинает есть много процессорного времени. Причём если включить побитовую инверсию, то скорость заметно возрастает.

Начинаем разбиратся. Чтобы избежать сложностей с позиционированием, данные рисуются побайтово. Перед этим каждый байт проходит соответствующую обработку, заданную пользователем: инверсия, разворот, etc. Т. к. данные выводятся побайтово, функция рисования дёргается довольно часто, и хоть это не должно создавать проблем, как гласит документация, основное время занимает именно сотня-другая вызовов этой функции. Проверим, в чём же дело.

Создадим приложение, рисующее символы с 0x00 по 0xFF. Конечно, всё, что ниже 0x20, а также 0x7F, будет отображаться символами-заменителями из шрифтового набора. Наследуемся от банального, базового для большей части графики, QWidget. Объявим две переменные, в которых будем хранить высоту и ширину одного символа:

qreal m_charHeight, m_charWidth;
. Нам это нужно для облегчения позиционирования. Опционально можно добавить ещё одну, для хранения размера шрифта:
int m_pointSize;
, хотя вполне можно обойтись и без неё. Добавим функцию, в которой рассчитаем всю метрику заранее, чтобы не тратить на это время при рисовании:

void textDrawingLags::updateMetrics()
{
 QFont font("Monospace", m_pointSize);
 QFontMetricsF fm(font);
 font.setStyleHint(QFont::TypeWriter);
 setFont(font);

 m_charHeight = fm.height() + fm.lineSpacing();
 m_charWidth  = fm.width(' ');
}

Теперь объявим слот, в котором будем устанавливать размер шрифта. Для незнающих: слот — это такая функция, которую можно соединить с сигналом в Qt.

void textDrawingLags::setPointSize(int size)
{
 if (size > 0) m_pointSize = size;
 updateMetrics();
 update();
}

Ну а теперь самое вкусное — рисование. Для этого нам потребуется переопределить метод paintEvent(). Выводить символы будем самым банальным способом, а именно вложенным циктом. Для того, чтобы выяснить, где же находится «бутылочное горлышко», будем считать время вывода каждого символа. Для этого воспользуемся функцией clock_gettime() из sys/time.h. В качестве первого аргумента требуется передать идентификатор определённых часов, над которыми производится действие, в нашем случае — считывание. Мы будем использовать CLOCK_MONOTONIC. Это (дальше копипаста из мана) «Часы, которые не могут быть настроены и показывают монотонный ход времени отсчитываемой с некой неопределённой начальной точки. Эти часы не подвержены скачкам системного времени (например, системный администратор вручную изменил время), но на них влияет постепенная подгонка, выполняемая adjtime(3) и NTP.». Конечно, можно использовать CLOCK_MONOTONIC_RAW, которые не подвержены влиянию подгонок, но они linux-only. Второй аргумент — указатель на структуру timespec, в которую будет производиться запись. В этой структуре всего два поля:

struct timespec {
   time_t   tv_sec;        /* секунды */
   long     tv_nsec;       /* наносекунды */
};

Таким образом мы можем получить время иполнения функции с наносекундной точностью с помощью всего лишь двух вызовов clock_gettime() и вычитания соответствующих полей. Реализуем:

void textDrawingLags::paintEvent(QPaintEvent *event)
{
 enum
  {
   lineWidth = 50
  };

QPainter painter(this);

timespec before, after;

for (int row = 0; row <= 255 / lineWidth; row++)
{
for (int col = 0; col < lineWidth; col++)
{
uchar byte = row * lineWidth + col;
clock_gettime(CLOCK_MONOTONIC, &before);
painter.drawText(QPointF(col * m_charWidth, (row + 1) * m_charHeight),
QString(byte));
clock_gettime(CLOCK_MONOTONIC, &after);
qDebug() << byte << (after.tv_sec - before.tv_sec) * 1000000000 +
(after.tv_nsec - before.tv_nsec);
}
}
}

Теперь добавим возможность менять размеры шрифта, чтобы хоть как-то наблюддать динамику. Я для этого создал объект класса QSlider и соединил его сигнал valueChanged(int) с нашим слотом setPointSize(int). Теперь то, ради чего вся эта простыня и писалась — результаты. Результаты у нас пишутся в qDebug, то есть на обычный stdout. Смотрим:

0 51693106
1 35842765
2 420025
3 158798
4 126142
5 105458
6 128760
7
Читать далее...
комментарии: 2 понравилось! вверх^ к полной версии