【问题标题】:Efficient tiny boolean matrix multiplication高效的微型布尔矩阵乘法
【发布时间】:2018-01-10 16:15:55
【问题描述】:

我有一些无符号 16 位整数 s 我想映射到一个无符号 32 位整数 r,这样s 中的每个翻转位最多翻转一个(给定)位r -- 只是0..160..32 之间的映射。所以我们可以把它看作一个矩阵方程

Ps = r

其中 P 是 32 x 16 布尔矩阵,s16 x 1 布尔向量,r32 x 1 布尔向量。我有一种直觉,我缺少一些超级简单的技巧。重要提示:目标机器是 16 位单片机!

这是我能做的最好的:

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] &amp; s) != 0x0000。我太愚蠢了,无法反汇编东西,但我猜如果我们不必做那个愚蠢的u32演员的话,这将是大约100条指令。但是话又说回来,也许编译器会为我们自动将循环分成两部分,在这种情况下,它对我们来说看起来相当不错。

为切线道歉,只是想我会分享我尝试的解决方案——你有更好的解决方案吗?

【问题讨论】:

  • 数学描述很好,但有点晦涩难懂。能举个例子吗?
  • 顺便说一句,请注意,您的文件范围数组P 无法通过函数调用进行初始化,如您的示例所示。在 C 中,函数调用可能只出现在函数体内。
  • 无论如何,您是说函数FsiPermutationHack() 正确实现了您想要的计算,但您正在寻找改进?在这种情况下,除非您对代码有特定问题,否则该问题可能更适合Code Review
  • 也许更适合代码审查交流?
  • 哦,顺便说一句,您需要在开始处理数组之前初始化变量r

标签: c matrix boolean


【解决方案1】:

如你所说,

如果我们没有的话,我猜这大约是 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;
}

假设 u16u32 分别是无符号的 16 位和 32 位整数,没有填充位。

还要注意,使用u16 类型而不是u32 执行算术应该是一种改进的想法是假设u32 类型具有比unsigned int 更高的整数提升等级。粗略地说,这归结为实现的unsigned int 是16 位类型。这对于 16 位处理器的实现来说是完全合理的。但是,在 intunsigned int 是 32 位类型的系统上,无论如何,所有更窄的整数算术参数都将提升为 32 位。


更新:

就更好的替代算法的可能性而言,我观察到结果的每一位都是从数组P 的不同元素中计算出来的,使用了每个元素的整个值,并且元素大小为与目标机器的本机字长相同。似乎没有比数组元素执行更少的 16 位按位与运算的空间(但请参见下文)。

如果我们接受每个数组元素必须单独处理,那么提供的实现可以很好地有效地处理它:

  • 在组装最终结果之前,它只执行 16 位计算;
  • 它在同一个循环中计算结果的上半部分和下半部分,因此只产生 16 次迭代的循环开销,而不是 32 次​​li>
  • 它在很大程度上消除了通过创建 P_hi 来访问数组的上半部分所需要的额外索引算法

可以手动展开循环以节省更多循环,但这是您绝对应该依赖编译器为您执行的优化。

至于“bit twiddling hacks”,我看到的任何这种性质的唯一范围是将相邻的 16 位数组元素对处理为 32 位无符号整数。这将允许执行一个 32 位按位与来代替每两个 16 位与。这将与两个 32 位比较相结合(vs. 上述代码中的两个 16 位比较)。可以保留上述方法的 16 位移位和按位或运算。除了由于违反严格的别名规则而导致形式上未定义的行为之外,这将涉及 32 位算术,这大概是 16 位机器上 16 位算术的一半。性能的衡量比预期的要好,但我认为没有理由期望这种方法会取得重大胜利。

【讨论】:

  • 这就是我所暗示的关于编译器也为我拆分它的评论。虽然不能接受它作为答案,因为我仍然怀疑存在一些巧妙的小技巧,从根本上改进了我的解决方案。另一个理想的答案可以解释为什么我提出的方法不太可能得到改进。
  • @BenjaminLindqvist,我已经添加了一些关于最优性的论证,但如果你没有被说服,那么我不知道我还能做什么。没有人可以将呈现的代码与您想象可能存在的未指定方法进行比较。您当然可以选择等待更多答案。如果你愿意,我可以建议你设置一个合理的时间限制,然后你接受你最喜欢的答案吗?
  • 不回答问题没有错。
  • 澄清一下。我想我们已经调查了我原来的解决方案的所有细微变化。但我不相信没有其他方法可以解决这个问题。
猜你喜欢
  • 2015-06-17
  • 2018-07-27
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-21
  • 2015-04-23
  • 2014-02-25
相关资源
最近更新 更多