我正在使用此函数将表示为布尔数组的8位二进制数转换为整数。有效吗?我正在嵌入式系统中使用它。它的性能还不错,但我对有些意见或建议(或更换(感兴趣。
uint8_t b2i( bool *bs ){
uint8_t ret = 0;
ret = bs[7] ? 1 : 0;
ret += bs[6] ? 2 : 0;
ret += bs[5] ? 4 : 0;
ret += bs[4] ? 8 : 0;
ret += bs[3] ? 16 : 0;
ret += bs[2] ? 32 : 0;
ret += bs[1] ? 64 : 0;
ret += bs[0] ? 128 : 0;
return ret;
}
如果没有特定的系统,就不可能说。拆卸代码,看看您得到了什么。在特定系统上对您的代码进行基准测试。这是了解手动优化的关键。
通常,有很多考虑因素。CPU的数据单词大小,指令集,编译器优化器性能,分支预测(如果有(,数据缓存(如果有(等
要使代码最佳地执行,无论数据字大小如何,您都可以将uint8_t
更改为uint_fast8_t
。除非您完全需要8位,否则将其作为uint8_t
。
缓存使用可能会或可能不会更有效,如果给出了上调整循环。无论如何,循环展开是一种旧的手动优化,我们不应该在现代编程中使用 - 编译器比程序员更有能力进行调用。
代码最糟糕的问题是众多分支。这些可能会导致瓶颈。
您的代码在以下X86机器代码gcc -O2
中产生:
b2i:
cmp BYTE PTR [rdi+6], 0
movzx eax, BYTE PTR [rdi+7]
je .L2
add eax, 2
.L2:
cmp BYTE PTR [rdi+5], 0
je .L3
add eax, 4
.L3:
cmp BYTE PTR [rdi+4], 0
je .L4
add eax, 8
.L4:
cmp BYTE PTR [rdi+3], 0
je .L5
add eax, 16
.L5:
cmp BYTE PTR [rdi+2], 0
je .L6
add eax, 32
.L6:
cmp BYTE PTR [rdi+1], 0
je .L7
add eax, 64
.L7:
lea edx, [rax-128]
cmp BYTE PTR [rdi], 0
cmovne eax, edx
ret
许多潜在的潜在效率低下的分支。我们可以通过使用循环使代码更快,更可读:
uint8_t b2i (const bool bs[8])
{
uint8_t result = 0;
for(size_t i=0; i<8; i++)
{
result |= bs[8-1-i] << i;
}
return result;
}
(理想情况下,Bool数组应首先从LSB排列,但这将改变代码的含义与原始代码相比(
提供此计算机代码:
b2i:
lea rsi, [rdi-8]
mov rax, rdi
xor r8d, r8d
.L2:
movzx edx, BYTE PTR [rax+7]
mov ecx, edi
sub ecx, eax
sub rax, 1
sal edx, cl
or r8d, edx
cmp rax, rsi
jne .L2
mov eax, r8d
ret
更多的说明,但分支更少。它可能会在X86和其他高端CPU上使用分支预测和指令缓存更好地执行您的代码。但是在8位微控制器上比您的代码更糟糕的是,只有指令总数很重要。
您也可以通过循环和位移动来进行此操作以减少代码重复:
int b2i(bool *bs) {
int ret = 0;
for (int i = 0; i < 8; i++) {
ret = ret << 1;
ret += bs[i];
}
return ret;
}