我有一些无符号的16位整数s
,我希望映射到无符号的32位整数r
,使s
中的每个翻转位都翻转r
中最多只有一个(给定)位 - 只是0..16
和0..32
之间的映射。所以我们可以将其视为矩阵方程
Ps = r
其中P是32 x 16
布尔矩阵,s
是16 x 1
布尔向量,r
是32 x 1
布尔向量。我有一种直觉,认为存在一些我失踪的超级简单黑客。重要提示:目标机器是16位mcu!
这是我能做的最好的事情:
static u16 P[32] = someArrayOrWhatever();
u32 FsiPermutationHack(u16 s) {
u32 r;
for (u16 i = 0; i < 32; i++)
{
r |= ((u32)((P[i] & s) > 0) << i);
}
return r;
}
理由是这样的:r
的第i位是1,当且仅当(P[i] & s) != 0x0000
时。我太愚蠢了,不能拆解东西,但我猜这将是〜100条说明如果我们没有做那个愚蠢的u32
演员。但话说回来,也许编译器会自动将循环拆分为两个,在这种情况下,它对我们来说看起来非常好。
为切线道歉,只是想我分享我的尝试解决方案 - 你有更好的解决方案吗?
答案 0 :(得分:3)
因为你说,
我猜这将是~100指令,如果我们没有 做那个愚蠢的u32演员。但话说回来,也许是编译器 为我们自动拆分循环,在这种情况下,它看起来很漂亮 对我们有好处。
和
我有一种直觉,认为存在一些我缺少的超级简单黑客
,我将解释你是如何在这个用于16位处理器的代码中尽量减少使用32位算法的。
你真的应该学习如何反汇编并检查编译结果,看看编译器是否会在你假设的情况下自动拆分循环,但假设它没有,我不明白为什么你不能这样做手动:
static u16 P[32]; /* value assigned elsewhere */
u32 FsiPermutationHack(u16 s) {
u16 *P_hi = P + 16;
u16 r_lo = 0;
u16 r_hi = 0;
for (u16 i = 0; i < 16; i++) {
r_lo |= (P[i] & s) != 0) << i;
r_hi |= (P_hi[i] & s) != 0) << i;
}
return ((u32) r_hi << 16) + r_lo;
}
假设u16
和u32
是无符号的16位和32位(分别)整数,没有填充位。
另请注意,使用u16
类型代替u32
执行算术的想法应该是一种改进,假设类型u32
的整数提升等级高于unsigned int
。粗略地说,这归结为实现的unsigned int
是16位类型。这对于16位处理器的实现来说是完全合理的。但是,在int
和unsigned int
而不是32位类型的系统上,无论如何,所有较窄的整数算术参数都将被提升为32位。
<强>更新强>
就更好的替代算法的可能性而言,我观察到结果的每个位都是从数组P
的不同元素计算出来的,即每个元素的整个值都被使用,并且该元素size与目标机器的本机字大小相同。似乎没有比数组元素执行更少的16位按位AND运算的余地(但见下文)。
如果我们接受每个数组元素必须单独处理,那么提供的实现可以很好地有效地处理它:
P_hi
以访问数组上半部分所需的额外索引算法可以手动展开循环以节省更多周期,但这是您绝对应该依赖编译器为您执行的优化类型。
至于“bit twddling hacks”,我认为对于任何这种性质的唯一范围是将相邻的16位数组元素处理为32位无符号整数。这将允许执行一个32位按位AND代替每两个16位AND。这将与两个32位比较相结合( vs。上述代码中的两个16位比较)。可以保留上述方法的16位移位和按位OR运算。除了由于违反严格别名规则而导致正式未定义的行为,这将涉及32位算术,大概是16位机器上16位算术的一半。绩效比预测的更好,但我认为没有理由期望从这种方法中取得重大胜利。