并行/组合的多个64位值的按位置换

时间:2011-08-20 02:24:58

标签: c algorithm parallel-processing permutation bit-shift

这个问题不是关于“我如何逐位排列”我们现在如何做到这一点,我们正在寻找的是一种更快的方式,更少的CPU指令,受到DES中sbox的bitlice实现的启发

为了加快一些密码,我们希望减少排列调用的数量。主要密码函数基于查找数组执行多个按位排列。由于置换操作只是位移,

我们的基本思想是采用需要相同排列的多个输入值,并将它们并行移位。例如,如果必须将输入位1移动到输出位6。

有没有办法做到这一点?我们现在没有示例代码,因为绝对不知道如何以高效的方式实现这一目标。

我们平台上的最大值大小为128位,最长输入值为64位。因此代码必须更快,然后进行128次整个排列。

修改

这是一个简单的8位排列示例

+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <= Bits
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <= Input
+---+---+---+---+---+---+---+---+
| 3 | 8 | 6 | 2 | 5 | 1 | 4 | 7 | <= Output
+---+---+---+---+---+---+---+---+

密码使用多个输入密钥。它是块密码,因此必须将相同的模式应用于输入的所有64位块。

由于每个输入块的排列相同,我们希望在一个步骤 /中处理多个输入块,以组合多个输入序列的操作。而不是每次呼叫移动128次一位,一次移动1次128位。

EDIT2

我们不能使用线程,因为我们必须在没有线程支持的情况下在嵌入式系统上运行代码。因此,我们也无法访问外部库,我们必须保持简单的C。

在测试并使用给定的答案后,我们通过以下方式完成:

  • 我们将128位64位值的单位放在uint128_t [64] *数组上。
  • 对于排列,我们只需要复制指针
  • 完成所有操作后,我们还原第一个操作并返回128个置换值

是的,这很简单。我们在项目早期就这样测试,但速度太慢了。好像我们在测试代码中有一个错误。

谢谢大家的提示和耐心。

4 个答案:

答案 0 :(得分:4)

通过使用8个将字节映射到64位字的查找表,您可以更快地使Stan的逐位代码。要从输入处理64位字,将其拆分为8个字节,然后从不同的查找表中查找每个字,然后查看结果。在我的计算机上,后者比32位排列的逐位方法快10倍。显然,如果您的嵌入式系统具有很少的缓存,那么 32 kB 16 kB的查找表可能是个问题。如果一次处理4位,则只需要16个16 * 8 = 128字节的查找表,即2 kB的查找表。

编辑:内部循环看起来像这样:

void permute(uint64_t* input, uint64_t* output, size_t n, uint64_t map[8][256])
{
    for (size_t i = 0; i < n; ++i) {
        uint8_t* p = (uint8_t*)(input+i);
        output[i] = map[0][p[0]] | map[1][p[1]] | map[2][p[2]] | map[3][p[3]]
            | map[4][p[4]] | map[5][p[5]] | map[6][p[6]] | map[7][p[7]];
    }
}

答案 1 :(得分:2)

我想你可能正在寻找bit-slicing implementation。这就是最快的DES破解影响的工作方式。 (或者无论如何,它都存在于SSE指令之前。)

我们的想法是以“按位”方式编写函数,将每个输出位表示为输入位上的布尔表达式。由于每个输出位仅取决于输入位,因此任何函数都可以用这种方式表示,甚至可以用加法,乘法或S-box查找来表示。

技巧是使用单个寄存器的实际位来表示来自多个输入字的单个位

我将用一个简单的四位函数来说明。

例如,假设您想要采用以下形式的四位输入:

x3 x2 x1 x0

...并为每个输入计算一个四位输出:

x2 x3 x2^x3 x1^x2

你想要为8个输入做这个。 (好的四位查找表最快。但这只是为了说明原理。)

假设你的八个输入是:

A = a3 a2 a1 a0
B = b3 b2 b1 b0
...
H = h3 h2 h1 h0

此处,a3 a2 a1 a0代表A输入的四位等。

首先,将所有8个输入编码为4个字节,其中每个字节保存8个输入中的每一个的一位:

X3 =  a3 b3 c3 d3 e3 f3 g3 h3
X2 =  a2 b2 c2 d2 e2 f2 g2 h2
X1 =  a1 b1 c1 d1 e1 f1 g1 h1
X0 =  a0 b0 c0 d0 e0 f0 g0 h0

此处,a3 b3 c3 ... h3X3的八位。它由所有八个输入的高位组成。 X2是所有八个输入的下一位。等等。

现在要并行计算八次函数,你只需:

Y3 = X2;
Y2 = X3;
Y1 = X2 ^ X3;
Y0 = X1 ^ X2;

现在Y3保持所有8个输出的高位,Y2保持所有8个输出的下一位,依此类推。我们只使用四个机器指令就在八个不同的输入上计算了这个函数!

更好的是,如果我们的CPU是32位(或64位),我们可以在32(或64)个输入上计算此功能,仍然只使用四个指令。

当然,编码输入并将输出解码到“位片”表示或从“位片”表示解码输出需要一些时间。但是对于正确的功能,这种方法提供了大量的位级并行性,从而实现了大规模的加速。

基本假设是你有许多输入(如32或64),你想要计算相同的函数,并且该函数既不太难也不容易表示为一堆布尔运算。 (太难使原始计算变慢;太容易使得时间由位片编码/解码本身占主导地位。)特别是对于密码学,其中(a)数据必须经历许多“轮次”处理,(b) )这个算法通常都是根据比特调整,而且(c)你在同一个数据上尝试了很多键...它通常效果很好。

答案 2 :(得分:1)

似乎很难在一次通话中进行排列。你的问题的一个特殊情况,倒转整数中的位,需要不止一个'调用'(调用是什么意思?)。有关此示例的信息,请参阅Bit Twiddling Hacks by Sean

如果您的映射模式并不复杂,也许您可​​以找到一种快速计算答案的方法:)但是,我不知道您是否喜欢这种直接方式:

#include <stdio.h>

unsigned char mask[8];

//map bit to position
//0 -> 2
//1 -> 7
//2 -> 5
//...
//7 -> 6
unsigned char map[8] = {
    2,7,5,1,4,0,3,6
};


int main()
{
    int i;

    //input:
    //--------------------
    //bit 7 6 5 4 3 2 1 0
    //--------------------
    //val 0 0 1 0 0 1 1 0
    //--------------------
    unsigned char input = 0x26;

    //so the output should be 0xA1:
    //    1 0 1 0 0 0 0 1
    unsigned char output;

    for(i=0; i<8; i++){ //initialize mask once
        mask[i] = 1<<i;
    }

    //do permutation
    output = 0;
    for(i=0; i<8; i++){
        output |= (input&mask[i])?mask[map[i]]:0;
    }

    printf("output=%x\n", output);
    return 0;
}

答案 3 :(得分:0)

您最好的选择是研究某种类型的线程方案...要么您可以使用消息传递系统,您将每个块发送到一组固定的工作线程,或者您可以使用非线程设置管道锁定单个生产者/消费者队列,以“同步”方式执行多个班次。我说“同步”是因为通用CPU上的管道不像你在固定功能设备上那样是真正的同步管道操作,但基本上对于给定的“片段”时间,每个线程都会工作同时处理多阶段问题的一个阶段,您可以将源数据“流”出管道。