下面是一段代码,如下所示:
// example_3
int Add_8K_3(int* in, int* out, int b)
{
int i;
for(i=0;i<1024;i++)
{
int a0, a1;
a0 = *in++;
a1 = *in++;
*out++ = a0 + b;
*out++ = a1 + b;
}
return 0;
}我用ARMCC和Xcode (用-O3)编译它。但是两个结果的性能有很大的不同。Xcode中的周期数大约是armcc结果的3倍。arm asm代码
{
Add_8K_3 PROC
ADD r0,r0,#4
MOV r3,#0x400
PUSH {r4} ;3264
|L1.12|
SUBS r3,r3,#1
LDR r4,[r0,#-4] ;3271
LDR r12,[r0],#8 ;3271
ADD r4,r4,r2 ;3271
STR r4,[r1],#8
ADD r12,r12,r2
STR r12,[r1,#-4]
BNE |L1.12|
POP {r4}
MOV r0,#0
BX lr
ENDP
}Xcode asm代码
{
_Add_8K_3:
.cfi_startproc
Lfunc_begin3:
.loc 1 77 0 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:77:0
@ BB#0:
.loc 1 76 19 prologue_end @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:76:19
push {r7, lr}
mov.w lr, #0
Ltmp15:
@DEBUG_VALUE: i <- 0+0
mov r7, sp
@DEBUG_VALUE: Add_8K_3:in <- R0+0
@DEBUG_VALUE: Add_8K_3:out <- R1+0
@DEBUG_VALUE: Add_8K_3:b <- R2+0
LBB3_1: @ =>This Inner Loop Header: Depth=1
Ltmp16:
@DEBUG_VALUE: Add_8K_3:in <- R0+0
@DEBUG_VALUE: Add_8K_3:out <- R1+0
@DEBUG_VALUE: Add_8K_3:b <- R2+0
@DEBUG_VALUE: i <- 0+0
.loc 1 82 9 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:82:9
ldr.w r12, [r0, lr, lsl #3]
Ltmp17:
@DEBUG_VALUE: a0 <- R12+0
add.w r3, r0, lr, lsl #3
.loc 1 83 9 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:83:9
ldr.w r9, [r3, #4]
Ltmp18:
@DEBUG_VALUE: a1 <- R9+0
.loc 1 86 9 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:86:9
add.w r3, r12, r2
str.w r3, [r1, lr, lsl #3]
add.w r12, r1, lr, lsl #3
Ltmp19:
.loc 1 79 20 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:79:20
add.w lr, lr, #1
Ltmp20:
@DEBUG_VALUE: i <- LR+0
.loc 1 87 9 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:87:9
add.w r3, r9, r2
str.w r3, [r12, #4]
Ltmp21:
.loc 1 79 9 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:79:9
cmp.w lr, #1024
bne LBB3_1
Ltmp22:
@ BB#2:
movs r0, #0
.loc 1 93 5 @ /Users/Emedia/Desktop/testperformance/testperformance/core.c:93:5
pop {r7, pc}
Ltmp23:
Lfunc_end3:
}我能得到的问题是Xcode不能通过合适的asm代码解析"inX“。我的问题是:(1)如何编写C代码来生成汇编语言代码,以便Xcode能够将"inX“解析成像arm这样的合适的汇编语言代码?(2)有没有手册来描述clang和armcc编译器的区别,并告诉我如何高性能地为iOS编写C代码。
谢谢。
发布于 2013-11-13 19:58:28
虽然苹果的LLVM能够生成不错的代码(比GCC好得多),但它仍然无法与同类中最好的ARMCC相匹敌。
事实上,ARMCC真的真的很好。
然而,你的样本是如此简单,即使是臭名昭著的GCC也不会失败。
有两件事你弄乱了你的代码:
NEON可以更快地处理示例函数,但是对于iOS部署,您需要同时使用AARCH32和AARCH64版本,而且两者在语法和寄存器分配方面有很大的不同。
如果你正在尝试学习手臂装配以进行优化,请不要这样做。AARCH64的ISA是完全重构和简化的,这意味着即使是世界上最糟糕的编译器,比如GCC,也能够生成相当不错的代码。
霓虹灯则是另一回事。这可能是值得的。(但您必须同时编写AARCH32和AARCH64版本)
发布于 2013-11-13 23:47:01
这对于向量化来说是一个完美的问题。正如Jake所指出的,矢量化有时会被避免,因为对于每个架构来说,拥有单独的代码路径是一件痛苦的事情。在理想的情况下,编译器会成功地自动向量化所有这些情况,这不会成为问题。同时,还有一些其他选项可供选择。
如果您的目标是iOS / OSX,并且可以将自己限制为clang,那么对于这样的简单循环,最好的解决方案是使用clang“扩展向量”;这些向量代码可以让您编写跨体系结构工作的向量代码:
typedef int vector_int __attribute__((ext_vector_type(4),aligned(4)));
const int ints_per_vector = 4;
int Add_8K_3(int *in, int* out, int b) {
vector_int *vin = (vector_int *)in;
vector_int *vout = (vector_int *)out;
for (int i=0; i<1024/ints_per_vector; i++)
vout[i] = vin[i] + b;
return 0;
}这会为clang支持的所有架构生成像样的(不完美的)向量代码。例如: armv7s:
0: adds r3, r0, r2
vld1.32 {d18, d19}, [r3]
adds r3, r1, r2
adds r2, #0x10
cmp.w r2, #0x1000
vadd.i32 q9, q9, q8
vst1.32 {d18, d19}, [r3]
bne 0barm64:
0: ldr q1, [x0, x8, lsl #4]
add.4s v1, v1, v0
str q1, [x1, x8, lsl #4]
add x8, x8, 1
cmp w8, #256
b.ne 0bx86_64:
0: movdqu (%rdi,%rax), %xmm1
paddd %xmm0, %xmm1
movdqu %xmm1, (%rsi,%rax)
add $0x10, %rax
cmp $0x1000, %eax
jne 0b但是,如果您需要代码可移植到其他编译器,最好使用内部函数或依赖于编译器优化,如果您确实需要代码尽可能快地运行,那么一些手动调优是不可避免的。
发布于 2013-11-13 20:35:40
我只能在这里猜测一下:
看起来,clang优化器更喜欢使用“立即偏移量”来访问输入和输出数组。例如:
str.w r3, [r1, lr, lsl #3]如果偏移量超过1024,clang优化器将选择双寄存器加载,并使用寄存器偏移量进行存储:
下面是当访问的整数范围超过1024 (在本例中为1025)时的反汇编:
0x79188: push {r4, r5, r7, lr}
0x7918a: add r7, sp, #8
0x7918c: str r8, [sp, #-4]!
0x79190: movw r3, #1025
0x79194: ldrd r8, r9, [r0]
0x79198: add.w r5, r9, r2
0x7919c: adds r0, #8
0x7919e: subs r3, #1
0x791a0: add.w r4, r8, r2
0x791a4: strd r4, r5, [r1]
0x791a8: add.w r1, r1, #8
0x791ac: bne 0x79194 ; Add_8K_3 + 12 at AppDelegate.m:24
0x791ae: movs r0, #0
0x791b0: ldr r8, [sp], #4
0x791b4: pop {r4, r5, r7, pc}当访问的整数范围超过1800 (for (int i = 0; i < 1800; ++i))时,clang将再次选择对单个寄存器加载和存储使用“立即偏移量”:
0x94180: push {r7, lr}
0x94182: mov.w lr, #0
0x94186: mov r7, sp
0x94188: ldr.w r12, [r0, lr, lsl #3]
0x9418c: add.w r3, r0, lr, lsl #3
0x94190: ldr.w r9, [r3, #4]
0x94194: add.w r3, r12, r2
0x94198: str.w r3, [r1, lr, lsl #3]
0x9419c: add.w r12, r1, lr, lsl #3
0x941a0: add.w lr, lr, #1
0x941a4: add.w r3, r9, r2
0x941a8: str.w r3, [r12, #4]
0x941ac: cmp.w lr, #1800
0x941b0: bne 0x94188 ; Add_8K_3 + 8 at AppDelegate.m:24
0x941b2: movs r0, #0
0x941b4: pop {r7, pc}不确定,为什么-也许是启发式的建议最佳的性能。
https://stackoverflow.com/questions/19950052
复制相似问题