Categories: C/C++

gcc-optimization/

Сегодня товарищ redp озадачил меня интересным вопросом. Дескать, если современные компиляторы такие умные, то почему GCC не в состоянии преобразовать даже элементарный макрос инверсии байт двойного слова в ассемблерную инструкцию bswap?

Речь идет о коде вроде этого:

#include <stdio.h>
#include <time.h>

typedef unsigned int u32 ;

#define U8TO32_BE(p)
(((u32)((p)[0]) << 24) |
((u32)((p)[1]) << 16) |
((u32)((p)[2]) <<  8) |
((u32)((p)[3])      ))

int main ( ) {
u32 x = ( u32 ) time ( 0 ) ;
printf ( «U8TO32_BE(%08x) = %08x n » , x ,
U8TO32_BE ( ( unsigned char * ) & x ) ) ;
return 0 ;
}

Действительно, как Visual Studio 2008, так и GCC 4.6 не в состоянии распознать в макросе U8TO32_BE простую команду bswap. Конечно, можно воспользоваться ассемблерными вставками или нестандартными расширениями языка типа _byteswap_ulong (не знаю, так ли оно называется в GCC), но эти методы плохи тем, что делают код зависимым от конкретного компилятора или архитектуры процессора.

Я переписал программу следующим образом:

#include <stdio.h>
#include <time.h>

typedef unsigned int u32 ;

#define BSWAP32(x) (
(((x) & 0xFF) << 24) |
(((x) & 0xFF00) << 8) |
(((x) & 0xFF0000) >> 8) |
(((x) & 0xFF000000) >> 24))

int main ( ) {
u32 x = ( u32 ) time ( 0 ) ;
printf ( «BSWAP32(%08x) = %08x n » , x , BSWAP32 ( x ) ) ;
return 0 ;
}

И посмотрел ассемблерный код, генерируемый GCC:

/ usr / local / bin / gcc46 -O2 -march =i686 -S -c bswap.c

Необходимо указать тип процессора, потому что в i386 команды bswap не было. По умолчанию GCC ничего и никак не оптимизирует, потому флаг оптимизации также необходим. В результате получаем файл bswap.s следующего содержания:

. file «bswap.c»
. section . rodata . str1 . 1 , «aMS» , @progbits , 1
. LC0 :
. string «BSWAP32(%08x) = %08xn»
. section . text . startup , «ax» , @progbits
. p2align 4 ,, 15
. globl  main
. type main , @function
main :
. LFB1 :
. cfi_startproc
pushl % ebp
. cfi_def_cfa_offset 8
. cfi_offset 5 , 8
movl % esp , % ebp
. cfi_def_cfa_register 5
andl  $ 16 , % esp
subl  $ 16 , % esp
movl $ 0 , ( % esp )
call time
movl $ . LC0 , ( % esp )
movl % eax , % edx
bswap % edx
movl % eax , 4 ( % esp )
movl % edx , 8 ( % esp )
call printf
xorl % eax , % eax
leave
. cfi_restore 5
. cfi_def_cfa 4 , 4
ret
. cfi_endproc
. LFE1 :
. size main , .- main
. ident «GCC: 4.6.2 20110729 (prerelease)»

Как видите, bswap появился. Что интересно, GCC 4.2 (который вышел в 2008-м году) так не умеет.

Мораль в том, что современные компиляторы хоть и умны, но не настолько, чтобы распознать в серии получения указателей на переменные и обращения к элементам массива простую перестановку байт (еще раз смотрим, что и как делает U8TO32_BE). Чем проще код вы пишите, тем легче компилятору будет его оптимизировать. Например, когда вы пишете цикл, обходящий массив, не нужно извращаться с указателями. Используйте обычные индексы.

admin

Share
Published by
admin
Tags: C/C++

Recent Posts

vim-commands/

Самое главное — побороть боязнь белого листа. Я всегда говорю это себе, когда нужно начать…

1 месяц ago

firefox-thunderbird-en-ru-dict/

По не вполне ясным причинам, Firefox умеет проверять орфографию либо только в русских, либо только…

1 месяц ago

perl-hacks/

Около месяца собирал разные «хаки» на языке программирования Perl. Эта подборка наглядно демонстрирует, как в…

1 месяц ago

perl-cy-check/

C недавних пор я стал увлекаться SEO. Порой передо мной встает задача быстро проверить индекс…

1 месяц ago

which-cms-perl/

Недавно написал несколько скриптов, позволяющих автоматически определять, какая CMS (Content Management System, система управления контентом)…

1 месяц ago

smtp-descr/

Я так подозреваю, что среди вас найдется те, кто скажет, что этот пост боян и…

1 месяц ago