Я скопировал ваши выводы: g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
, Linux 2.6.38-10-generic #46-Ubuntu x86_64
на моем Core 2 Duo.Результаты могут отличаться в зависимости от версии вашего компилятора и процессора.Я получаю ~ 26 и ~ 9.
Когда я указываю -march = native в опциях компилятора, сгенерированный двоичный файл работает в 2,7 раза медленнее.Это почему ?
Поскольку -march = встроенная версия компилируется (можно найти с помощью objdump -D
, вы также можете использовать gcc -S -fverbose-asm
):
rep movsq %ds:(%rsi),%es:(%rdi) ; where rcx = 128 / 8
И версия без компилируется в16 пар загрузки / хранения, например:
mov 0x20(%rbp),%rdx
mov %rdx,0x20(%rbx)
Что, по-видимому, быстрее на наших компьютерах.
Во всяком случае, я бы ожидал, что -march = native произведет оптимизированный код.
В этом случае оказалось, что это пессимизация в пользу rep movsq
за серию ходов, но это не всегда так.Первая версия короче, что может быть лучше в некоторых (большинстве?) Случаях.Или это может быть ошибка в оптимизаторе.
Существуют ли другие функции, которые могут показывать этот тип поведения?
Любая функция, для которой сгенерированный код отличается, когда вы указываете -march=native
, подозревает, что включает функции, реализованные какмакросы или статические заголовки, имя которых начинается с __builtin
.Возможно также (с плавающей запятой) математические функции.
Другим интересным моментом является то, что если размер> 32 * 4, то нет разницы между временем выполнения сгенерированных таким образом двоичных файлов
Это потому, что они оба компилируютсядо rep movsq
, 128, вероятно, самый большой размер, для которого GCC будет генерировать серию загрузки / хранения (было бы интересно посмотреть, если это также для других платформ).Кстати, когда компилятор не знает размер во время компиляции (например, int size=atoi(argv[1]);
), он просто превращается в вызов memcpy
с переключателем или без него.