GCCを使用すると、mem_demo_1は60行のアセンブリにコンパイルされ、mem_demo_2は20行にコンパイルされます。パフォーマンスの違いも大きくなります。
Linux2.6.32とgcc4.4.6でこのステートメントを検証することにしました。初め
mem_demo_1は60行のアセンブリにコンパイルされ、mem_demo_2は20行にコンパイルされます
。
これは(ファイルmain.cの)テストです:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* mem_demo_1(char *j)
{
// *BAD* compiler cannot tell pointer alignment, must test
memset(j, 0, 64);
return j;
}
char* mem_demo_2(void)
{
// *GOOD* compiler can tell pointer alignment
char * j = malloc(64);
memset(j, 0, 64);
return j;
}
int main()
{
char *p;
p = malloc(64);
p = mem_demo_1(p);
printf ("%p\n",p);
free (p);
p = mem_demo_2();
printf ("%p\n",p);
free (p);
return 0;
}
私がコンパイルするとき:
gcc -fno-inline -fno-builtin -m64 -g -O2 main.c -o main.no_inline_no_builtin
mem_demo_1には8行しかないことがわかります。
(gdb) disassemble mem_demo_1
Dump of assembler code for function mem_demo_1:
0x00000000004005d0 <+0>: push %rbx
0x00000000004005d1 <+1>: mov $0x40,%edx
0x00000000004005d6 <+6>: mov %rdi,%rbx
0x00000000004005d9 <+9>: xor %esi,%esi
0x00000000004005db <+11>: callq 0x400470 <memset@plt>
0x00000000004005e0 <+16>: mov %rbx,%rax
0x00000000004005e3 <+19>: pop %rbx
0x00000000004005e4 <+20>: retq
End of assembler dump.
mem_demo_2には11行しかないことがわかります。
(gdb) disassemble mem_demo_2
Dump of assembler code for function mem_demo_2:
0x00000000004005a0 <+0>: push %rbx
0x00000000004005a1 <+1>: mov $0x40,%edi
0x00000000004005a6 <+6>: callq 0x400480 <malloc@plt>
0x00000000004005ab <+11>: mov $0x40,%edx
0x00000000004005b0 <+16>: mov %rax,%rbx
0x00000000004005b3 <+19>: xor %esi,%esi
0x00000000004005b5 <+21>: mov %rax,%rdi
0x00000000004005b8 <+24>: callq 0x400470 <memset@plt>
0x00000000004005bd <+29>: mov %rbx,%rax
0x00000000004005c0 <+32>: pop %rbx
0x00000000004005c1 <+33>: retq
End of assembler dump.
したがって、「mem_demo_1は60行のアセンブリにコンパイルされ、mem_demo_2は20行にコンパイルされます」は確認できません。
私がコンパイルするとき:
gcc -m64 -g -O2 main.c -o main.default
gccは独自のmemsetの実装を使用しており、関数mem_demo_1とmem_demo_2の両方が大きくなっています。
mem_demo_1: 43 instructions
mem_demo_2: 48 instructions
ただし、「mem_demo_1は60行のアセンブリにコンパイルされ、mem_demo_2は20行にコンパイルされます」も確認できません。
2番
「パフォーマンスの違いも大きい」
memsetでたくさんのループを行うためにmain.cを拡張しました。また、mem_demo_1のmemsetがmem_demo_2のmemsetよりも遅いこともわかりません。これはLinuxのperfレポートからのものです:
mem_demo_2はmemsetで8.37%を費やしています:
8.37%main.perf.no_bulibc-2.12.so[。]__memset_sse2
mem_demo_1はmemsetで7.61%を費やしています:
7.61%main.perf.no_bulibc-2.12.so[。]__memset_sse2
そして、これらはそれ自体の測定値です。
# time ./main.perf.no_builtin_no_inline 100000000 1 0
number loops 100000000
mem_demo_1
real 0m3.483s
user 0m3.481s
sys 0m0.002s
# time ./main.perf.no_builtin_no_inline 100000000 2 0
number loops 100000000
mem_demo_2
real 0m3.503s
user 0m3.501s
sys 0m0.001s
ちなみに、これはgcc -fverbose-asm -c -S -O3
mem_demo_2のアセンブラを表示する方法です。
char* mem_demo_2(void)
{
char * j = malloc(64);
memset(j, 0, 64);
return j;
}
.file "main.mem_demo_2.c"
# GNU C (GCC) version 4.4.6 20110731 (Red Hat 4.4.6-3) (x86_64-redhat-linux)
# compiled by GNU C version 4.4.6 20110731 (Red Hat 4.4.6-3), GMP version 4.3.1, MPFR version 2.4.1.
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
# options passed: main.mem_demo_2.c -m64 -mtune=generic -auxbase-strip
# main.mem_demo_2.default.asm -g -O3 -fverbose-asm
# options enabled: -falign-loops -fargument-alias
# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg
# -fcaller-saves -fcommon -fcprop-registers -fcrossjumping
# -fcse-follow-jumps -fdefer-pop -fdelete-null-pointer-checks
# -fdwarf2-cfi-asm -fearly-inlining -feliminate-unused-debug-types
# -fexpensive-optimizations -fforward-propagate -ffunction-cse -fgcse
# -fgcse-after-reload -fgcse-lm -fguess-branch-probability -fident
# -fif-conversion -fif-conversion2 -findirect-inlining -finline
# -finline-functions -finline-functions-called-once
# -finline-small-functions -fipa-cp -fipa-cp-clone -fipa-pure-const
# -fipa-reference -fira-share-save-slots -fira-share-spill-slots -fivopts
# -fkeep-static-consts -fleading-underscore -fmath-errno -fmerge-constants
# -fmerge-debug-strings -fmove-loop-invariants -fomit-frame-pointer
# -foptimize-register-move -foptimize-sibling-calls -fpeephole -fpeephole2
# -fpredictive-commoning -freg-struct-return -fregmove -freorder-blocks
# -freorder-functions -frerun-cse-after-loop -fsched-interblock
# -fsched-spec -fsched-stalled-insns-dep -fschedule-insns2 -fsigned-zeros
# -fsplit-ivs-in-unroller -fsplit-wide-types -fstrict-aliasing
# -fstrict-overflow -fthread-jumps -ftoplevel-reorder -ftrapping-math
# -ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-coalesce-vars
# -ftree-copy-prop -ftree-copyrename -ftree-cselim -ftree-dce
# -ftree-dominator-opts -ftree-dse -ftree-fre -ftree-loop-im
# -ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
# -ftree-pre -ftree-reassoc -ftree-scev-cprop -ftree-sink -ftree-sra
# -ftree-switch-conversion -ftree-ter -ftree-vect-loop-version
# -ftree-vectorize -ftree-vrp -funit-at-a-time -funswitch-loops
# -funwind-tables -fvar-tracking -fvar-tracking-assignments
# -fvect-cost-model -fverbose-asm -fzero-initialized-in-bss
# -m128bit-long-double -m64 -m80387 -maccumulate-outgoing-args
# -malign-stringops -mfancy-math-387 -mfp-ret-in-387 -mfused-madd -mglibc
# -mieee-fp -mmmx -mno-sse4 -mpush-args -mred-zone -msse -msse2
# -mtls-direct-seg-refs
mem_demo_2:
.LFB30:
.file 1 "main.mem_demo_2.c"
.loc 1 6 0
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
.loc 1 7 0
movl $64, %edi
call malloc
.loc 1 8 0
testb $1, %al
.loc 1 7 0
movq %rax, %rsi
.LVL0:
.loc 1 8 0
movq %rax, %rdi
movl $64, %edx
jne .L10
testb $2, %dil
jne .L11
.L3:
testb $4, %dil
jne .L12
.L4:
movl %edx, %ecx
xorl %eax, %eax
.LVL1:
shrl $3, %ecx
testb $4, %dl
mov %ecx, %ecx
rep stosq
je .L5
movl $0, (%rdi)
addq $4, %rdi
.L5:
testb $2, %dl
je .L6
movw $0, (%rdi)
addq $2, %rdi
.L6:
andl $1, %edx
je .L7
movb $0, (%rdi)
.L7:
.loc 1 10 0
movq %rsi, %rax
addq $8, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 8
ret
.p2align 4,,10
.p2align 3
.L10:
.cfi_restore_state
.loc 1 8 0
leaq 1(%rax), %rdi
movb $0, (%rax)
movb $63, %dl
testb $2, %dil
je .L3
.p2align 4,,10
.p2align 3
.L11:
movw $0, (%rdi)
addq $2, %rdi
subl $2, %edx
testb $4, %dil
je .L4
.p2align 4,,10
.p2align 3
.L12:
movl $0, (%rdi)
subl $4, %edx
addq $4, %rdi
jmp .L4
.cfi_endproc