Оптимизация gcc при копировании массива - PullRequest
3 голосов
/ 08 мая 2011

Мне нужно профилировать приложение, которое выполняет много копий массивов, поэтому я профилировал эту очень простую функцию:

typedef unsigned char UChar;
void copy_mem(UChar *src, UChar *dst, unsigned int len) {
        UChar *end = src + len;
        while (src < end)
                *dst++ = *src++;
}

Я использую Intel VTune, чтобы выполнить фактическое профилирование, и изтам я видел, что есть существенные различия при компиляции с gcc -O3 и "обычным" gcc (4.4).

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

Это неоптимизированная версия:

.L3:
        movl    8(%ebp), %eax
        movzbl  (%eax), %edx
        movl    12(%ebp), %eax
        movb    %dl, (%eax)
        addl    $1, 12(%ebp)
        addl    $1, 8(%ebp)
.L2:
        movl    8(%ebp), %eax
        cmpl    -4(%ebp), %eax
        jb      .L3
        leave

Итак, я вижу, что сначала она загружает меч из * src и помещает младший байт в edx, затем сохраняет его в * dst и обновляетуказатели: достаточно просто.

Затем я увидел оптимизированную версию и ничего не понял.

РЕДАКТИРОВАТЬ : здесь естьоптимизированная сборка.

Поэтому у меня такой вопрос: какие виды оптимизации gcc может выполнять в этой функции?

Ответы [ 5 ]

2 голосов
/ 09 мая 2011

Этот оптимизированный код довольно беспорядочный, но я могу заметить 3 цикла (около L6, L13 и L12).Я думаю, что gcc делает то, что предложил @GJ (я проголосовал за него).Цикл около L6 перемещается на 4 байта каждый раз, в то время как цикл № 2 перемещается только на один байт и выполняется только иногда после цикла # 1.Я все еще не могу получить цикл № 3, поскольку он идентичен циклу № 2.

2 голосов
/ 08 мая 2011

Ваша неоптимизированная функция перемещает байт на байт!

Если вы сначала вычислите длину, то вы можете переместить 4 байта одновременно, остальные 1,3 байта перемещаются вручную.Если вы можете обеспечить правильное (4-байтовое) выравнивание памяти, функция копирования также должна быть быстрее.И нет необходимости увеличивать указатели в стеке, вы можете использовать регистры.Все это думает, что значительно улучшит скорость функции.

Или используйте специальные функции перемещения памяти, такие как memmove!

1 голос
/ 08 мая 2011

Что ж, типы оптимизаций зависят от функции и ее свойств. Если бы функция была помечена как встроенная и была достаточно маленькой, она превратилась бы в развернутый цикл с MOV, который быстрее, чем на основе REPварианты (и это может избежать разлива регистра).для неизвестных размеров вы получаете семейство инструкций REP MOVS (начиная с самого большого размера слова, чтобы уменьшить количество циклов для постоянного размера, иначе он будет использовать размер блока данных, который вы копируете).

Если SSE включен, он, скорее всего, будет использовать либо развернутые движения без выравнивания (MOVDQU), где длина разрешает, либо зацикленные движения без выравнивания (не знаю, если он будет использовать временную предварительную выборку, выигрыш от этого зависитразмер блока), если длина достаточно велика.если source / dest выровнены правильно, он попытается использовать более быстрые выровненные варианты.

На данный момент самое лучшее, что вы можете получить для этой функции, это MOVSB, когда она не встроена.

0 голосов
/ 08 мая 2011

Вы также можете воспользоваться restrict здесь.

0 голосов
/ 08 мая 2011

Самые быстрые инструкции по сборке x86, которые может сгенерировать gcc, будут rep movsd, что будет копировать 4 байта за раз.Стандартная функция libc memcpy в <string.h> вместе со специальным встроенным gcc делает для memcpy и многие другие функции в <string.h> дают вам максимально быстрый результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...