生活随笔
收集整理的這篇文章主要介紹了
C语言 使用数组索引与指针索引 在循环中对编译器优化的影响及耗时分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C語言在訪問數組時既可以使用如a[i]這樣的下標方式,也可以使用*(a+i)這樣的指針方式,理論上完全等價。但是在編譯器對循環作優化時,對于指針方式的索引很有可能分析不徹底,因此相比數組索引耗時有所增加
數組索引耗時
# include <stdio.h>
# include <stdlib.h>
# include <time.h> unsigned long get_start_ms ( ) { struct timespec ts
; clock_gettime ( CLOCK_MONOTONIC
, & ts
) ; return ( ts
. tv_sec
* 1000 + ts
. tv_nsec
/ 1000000 ) ;
} int main ( ) { unsigned long t
= get_start_ms ( ) ; uint64_t * mem
= malloc ( 1024 * 1024 * 128 * sizeof ( uint64_t ) ) ; register uint64_t sum
= 0 ; for ( int i
= 0 ; i
< 1024 * 1024 * 128 ; i
++ ) sum
+= mem
[ i
] ; printf ( "[%lums]0x%016llx\n" , get_start_ms ( ) - t
, sum
) ;
}
分別編譯、運行后結果如下所示 可見隨著優化等級提升,用時依次減少。
指針索引耗時
# include <stdio.h>
# include <stdlib.h>
# include <time.h> unsigned long get_start_ms ( ) { struct timespec ts
; clock_gettime ( CLOCK_MONOTONIC
, & ts
) ; return ( ts
. tv_sec
* 1000 + ts
. tv_nsec
/ 1000000 ) ;
} int main ( ) { unsigned long t
= get_start_ms ( ) ; uint64_t * mem
= malloc ( 1024 * 1024 * 128 * sizeof ( uint64_t ) ) ; uint64_t * end
= mem
+ 1024 * 1024 * 128 ; register uint64_t sum
= 0 ; while ( mem
< end
) sum
+= * mem
++ ; printf ( "[%lums]0x%016llx\n" , get_start_ms ( ) - t
, sum
) ;
}
分別編譯、運行后結果如下所示 同樣,隨著優化等級提升,用時依次減少。但是我們注意到,在-O1時指針與數組用時大體相同,但是之后兩級都是數組比指針快約10ms,不是一個小數目。
編譯器優化分析
對于出現這種現象的原因,我們需要從匯編代碼作分析。
1. 優化等級-O1
在這一級,二者區別不明顯。
2. 優化等級-O2
在這一級,引入了向量化,但是數組索引的向量化程度更高,指令條數也更少。
cmpq $4
, %r9jae LBB1_2
## %bb.1
: ##
implicit-def : $rbxjmp LBB1_11
LBB1_2 : movq %r9
, %r8andq $-4
, %r8leaq
-4 ( %r8
) , %rdimovq %rdi
, %rsishrq $2
, %rsiincq %rsimovl %esi
, %ebxandl $3
, %ebxcmpq $12
, %rdijae LBB1_4
## %bb.3
: pxor %xmm0
, %xmm0xorl %edi
, %edipxor %xmm1
, %xmm1testq %rbx
, %rbxjne LBB1_7jmp LBB1_9
LBB1_4 : movl $1
, %edisubq %rsi
, %rdileaq
-1 ( %rbx
, %rdi
) , %rsipxor %xmm0
, %xmm0xorl %edi
, %edipxor %xmm1
, %xmm1.p2align 4
, 0x90
LBB1_5 : ## =>This Inner Loop
Header : Depth=1movdqu
8 ( %rax
, %rdi
, 8
) , %xmm2paddq %xmm0
, %xmm2movdqu
24 ( %rax
, %rdi
, 8
) , %xmm0paddq %xmm1
, %xmm0movdqu
40 ( %rax
, %rdi
, 8
) , %xmm1movdqu
56 ( %rax
, %rdi
, 8
) , %xmm3movdqu
72 ( %rax
, %rdi
, 8
) , %xmm4paddq %xmm1
, %xmm4paddq %xmm2
, %xmm4movdqu
88 ( %rax
, %rdi
, 8
) , %xmm2paddq %xmm3
, %xmm2paddq %xmm0
, %xmm2movdqu
104 ( %rax
, %rdi
, 8
) , %xmm0paddq %xmm4
, %xmm0movdqu
120 ( %rax
, %rdi
, 8
) , %xmm1paddq %xmm2
, %xmm1addq $16
, %rdiaddq $4
, %rsijne LBB1_5
## %bb.6
: testq %rbx
, %rbxje LBB1_9
LBB1_7 : leaq
24 ( %rax
, %rdi
, 8
) , %raxnegq %rbx.p2align 4
, 0x90
LBB1_8 : ## =>This Inner Loop
Header : Depth=1movdqu
-16 ( %rax
) , %xmm2paddq %xmm2
, %xmm0movdqu
( %rax
) , %xmm2paddq %xmm2
, %xmm1addq $32
, %raxincq %rbxjne LBB1_8
LBB1_9 : paddq %xmm1
, %xmm0pshufd $78
, %xmm0
, %xmm1 ## xmm1 = xmm0[2
, 3
, 0
, 1]paddq %xmm0
, %xmm1movq %xmm1
, %rbxcmpq %r8
, %r9je LBB1_12
## %bb.10
: leaq
( %rdx
, %r8
, 8
) , %rdx.p2align 4
, 0x90
LBB1_11 : ## =>This Inner Loop
Header : Depth=1addq
( %rdx
) , %rbxaddq $8
, %rdxcmpq %rcx
, %rdxjb LBB1_11
LBB1_12 :
3. 優化等級-O2 -march=native
在這一級引入了avx512,仍然是數組索引的向量化程度更高,指令條數更少。
cmpq $16
, %r9jae LBB1_2
## %bb.1
: ##
implicit-def : $rbxjmp LBB1_11
LBB1_2 : movq %r9
, %r8andq $-16
, %r8leaq
-16 ( %r8
) , %rdimovq %rdi
, %rsishrq $4
, %rsiaddq $1
, %rsimovl %esi
, %ebxandl $3
, %ebxcmpq $48
, %rdijae LBB1_4
## %bb.3
: vpxor %xmm0
, %xmm0
, %xmm0xorl %edi
, %edivpxor %xmm1
, %xmm1
, %xmm1vpxor %xmm2
, %xmm2
, %xmm2vpxor %xmm3
, %xmm3
, %xmm3testq %rbx
, %rbxjne LBB1_7jmp LBB1_9
LBB1_4 : movl $1
, %edisubq %rsi
, %rdileaq
( %rbx
, %rdi
) , %rsiaddq $-1
, %rsivpxor %xmm0
, %xmm0
, %xmm0xorl %edi
, %edivpxor %xmm1
, %xmm1
, %xmm1vpxor %xmm2
, %xmm2
, %xmm2vpxor %xmm3
, %xmm3
, %xmm3.p2align 4
, 0x90
LBB1_5 : ## =>This Inner Loop
Header : Depth=1vpaddq
8 ( %rax
, %rdi
, 8
) , %ymm0
, %ymm0vpaddq
40 ( %rax
, %rdi
, 8
) , %ymm1
, %ymm1vpaddq
72 ( %rax
, %rdi
, 8
) , %ymm2
, %ymm2vpaddq
104 ( %rax
, %rdi
, 8
) , %ymm3
, %ymm3vpaddq
136 ( %rax
, %rdi
, 8
) , %ymm0
, %ymm0vpaddq
168 ( %rax
, %rdi
, 8
) , %ymm1
, %ymm1vpaddq
200 ( %rax
, %rdi
, 8
) , %ymm2
, %ymm2vpaddq
232 ( %rax
, %rdi
, 8
) , %ymm3
, %ymm3vpaddq
264 ( %rax
, %rdi
, 8
) , %ymm0
, %ymm0vpaddq
296 ( %rax
, %rdi
, 8
) , %ymm1
, %ymm1vpaddq
328 ( %rax
, %rdi
, 8
) , %ymm2
, %ymm2vpaddq
360 ( %rax
, %rdi
, 8
) , %ymm3
, %ymm3vpaddq
392 ( %rax
, %rdi
, 8
) , %ymm0
, %ymm0vpaddq
424 ( %rax
, %rdi
, 8
) , %ymm1
, %ymm1vpaddq
456 ( %rax
, %rdi
, 8
) , %ymm2
, %ymm2vpaddq
488 ( %rax
, %rdi
, 8
) , %ymm3
, %ymm3addq $64
, %rdiaddq $4
, %rsijne LBB1_5
## %bb.6
: testq %rbx
, %rbxje LBB1_9
LBB1_7 : leaq
( %rax
, %rdi
, 8
) , %raxaddq $104
, %raxnegq %rbx.p2align 4
, 0x90
LBB1_8 : ## =>This Inner Loop
Header : Depth=1vpaddq
-96 ( %rax
) , %ymm0
, %ymm0vpaddq
-64 ( %rax
) , %ymm1
, %ymm1vpaddq
-32 ( %rax
) , %ymm2
, %ymm2vpaddq
( %rax
) , %ymm3
, %ymm3subq $-128
, %raxincq %rbxjne LBB1_8
LBB1_9 : vpaddq %ymm3
, %ymm1
, %ymm1vpaddq %ymm2
, %ymm0
, %ymm0vpaddq %ymm1
, %ymm0
, %ymm0vextracti128 $1
, %ymm0
, %xmm1vpaddq %ymm1
, %ymm0
, %ymm0vpshufd $78
, %xmm0
, %xmm1 ## xmm1 = xmm0[2
, 3
, 0
, 1]vpaddq %xmm1
, %xmm0
, %xmm0vmovq %xmm0
, %rbxcmpq %r8
, %r9je LBB1_12
## %bb.10
: leaq
( %rdx
, %r8
, 8
) , %rdx.p2align 4
, 0x90
LBB1_11 : ## =>This Inner Loop
Header : Depth=1addq
( %rdx
) , %rbxaddq $8
, %rdxcmpq %rcx
, %rdxjb LBB1_11
LBB1_12 :
由此可見,編譯器對for循環下的數組索引的向量化有一套成熟的優化手段,貿然改用指針索引反而會拖慢速度,一定要慎之又慎
總結
以上是生活随笔 為你收集整理的C语言 使用数组索引与指针索引 在循环中对编译器优化的影响及耗时分析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。