嵌套 if 与 for 循环的速度

abe*_*man 5 c embedded performance interrupt pic

我最近在中断服务例程(ISR)中遇到了这段代码:

\n
#define MAX_CHANNELS 4\nstatic uint16_t volatile* ADCVALS[MAX_CHANNELS] = {\n    &ADC1BUF0, &ADC1BUF1, &ADC1BUF2, &ADC1BUF3\n};\nstatic uint8_t CHANNELS = 0;\nstatic uint16_t volatile* volatile BUFFER_IDX[MAX_CHANNELS];\n\nvoid __attribute__((interrupt, no_auto_psv)) _AD1Interrupt(void) {\n    *(BUFFER_IDX[0]++) = *ADCVALS[0];\n    if (CHANNELS >= 1) {\n        *(BUFFER_IDX[1]++) = *ADCVALS[1];\n        if (CHANNELS >= 2) {\n            *(BUFFER_IDX[2]++) = *ADCVALS[2];\n             if (CHANNELS >= 3) {\n                *(BUFFER_IDX[3]++) = *ADCVALS[3];\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

它将 1-4 个寄存器值复制到内存中,具体取决于 的值CHANNELS,该值是 0-3 之间的值,通过 setter 函数在程序中的其他位置设置。

\n

我发现嵌套的 if 非常丑陋,并将其更改为:

\n
int i;\nfor (i = 0; i <= CHANNELS; i++) {\n    *(BUFFER_IDX[i]++) = *ADCVALS[i];\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这立即打破了ISR。这是一个嵌入式系统,PIC24 架构,64 MHz 时钟。ISR 的时间受到严格限制,必须在 1 \xc2\xb5 秒内完成。for 循环显然太慢,而嵌套的 if 足够快。

\n

那么我的问题有两个:

\n
    \n
  1. 有没有一种不太难看的方法来完成嵌套 if 子句的操作,而又不会减慢 ISR 的速度?
  2. \n
  3. 为什么 for 循环慢得多?我本来期望编译器(xc16)足够聪明,可以为两者生成类似的 asm(在-O2)。
  4. \n
\n

小智 5

for (int i = 0; i <= CHANNELS; i++) {
    *(BUFFER_IDX[i]++) = *ADCVALS[i];
}
Run Code Online (Sandbox Code Playgroud)

        mov     DWORD PTR [rbp-4], 0
        jmp     .L2
.L3:
        mov     eax, DWORD PTR [rbp-4]
        cdqe
        mov     rax, QWORD PTR [rbp-80+rax*8]
        mov     edx, DWORD PTR [rax]
        mov     eax, DWORD PTR [rbp-4]
        cdqe
        mov     rax, QWORD PTR [rbp-48+rax*8]
        lea     rsi, [rax+4]
        mov     ecx, DWORD PTR [rbp-4]
        movsx   rcx, ecx
        mov     QWORD PTR [rbp-48+rcx*8], rsi
        mov     DWORD PTR [rax], edx
        add     DWORD PTR [rbp-4], 1
.L2:
        mov     eax, DWORD PTR [rbp-4]
        cmp     eax, DWORD PTR [rbp-8]
        jle     .L3
Run Code Online (Sandbox Code Playgroud)

*(BUFFER_IDX[0]++) = *ADCVALS[0];
if (CHANNELS >= 1) {
    *(BUFFER_IDX[1]++) = *ADCVALS[1];
    if (CHANNELS >= 2) {
        *(BUFFER_IDX[2]++) = *ADCVALS[2];
        if (CHANNELS >= 3) {
            *(BUFFER_IDX[3]++) = *ADCVALS[3];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mov     rax, QWORD PTR [rbp-80]
mov     edx, DWORD PTR [rax]
mov     rax, QWORD PTR [rbp-48]
lea     rcx, [rax+4]
mov     QWORD PTR [rbp-48], rcx
mov     DWORD PTR [rax], edx
cmp     DWORD PTR [rbp-4], 0
jle     .L2
mov     rax, QWORD PTR [rbp-72]
mov     edx, DWORD PTR [rax]
mov     rax, QWORD PTR [rbp-40]
lea     rcx, [rax+4]
mov     QWORD PTR [rbp-40], rcx
mov     DWORD PTR [rax], edx
cmp     DWORD PTR [rbp-4], 1
jle     .L2
mov     rax, QWORD PTR [rbp-64]
mov     edx, DWORD PTR [rax]
mov     rax, QWORD PTR [rbp-32]
lea     rcx, [rax+4]
mov     QWORD PTR [rbp-32], rcx
mov     DWORD PTR [rax], edx
cmp     DWORD PTR [rbp-4], 2
jle     .L2
mov     rax, QWORD PTR [rbp-56]
mov     edx, DWORD PTR [rax]
mov     rax, QWORD PTR [rbp-24]
lea     rcx, [rax+4]
mov     QWORD PTR [rbp-24], rcx
mov     DWORD PTR [rax], edx
Run Code Online (Sandbox Code Playgroud)

你如何看到嵌套的 if 会做更少的跳转,但当前的编译器可以优化它,并且使用 -O3 标志你会得到类似的东西

        mov     eax, DWORD PTR [rsp+12]
        test    eax, eax
        js      .L2
        mov     rax, QWORD PTR [rsp+48]
        mov     edx, DWORD PTR [rax]
        mov     rax, QWORD PTR [rsp+16]
        mov     DWORD PTR [rax], edx
        mov     eax, DWORD PTR [rsp+12]
        test    eax, eax
        jle     .L2
        mov     rax, QWORD PTR [rsp+56]
        mov     edx, DWORD PTR [rax]
        mov     rax, QWORD PTR [rsp+24]
        mov     DWORD PTR [rax], edx
        mov     eax, DWORD PTR [rsp+12]
        sub     eax, 1
        jle     .L2
        mov     rax, QWORD PTR [rsp+64]
        mov     edx, DWORD PTR [rax]
        mov     rax, QWORD PTR [rsp+32]
        mov     DWORD PTR [rax], edx
        mov     eax, DWORD PTR [rsp+12]
        cmp     eax, 2
        jle     .L2
        mov     rax, QWORD PTR [rsp+72]
        mov     edx, DWORD PTR [rax]
        mov     rax, QWORD PTR [rsp+40]
        mov     DWORD PTR [rax], edx
        mov     eax, DWORD PTR [rsp+12]
.L2:
Run Code Online (Sandbox Code Playgroud)

与嵌套 if-s 具有 +- 相同的性能

  • 某些 x86 和 PIC 的性能有很大不同。例如,为了支持分支预测而进行优化是完全没有意义的。 (3认同)