【问题标题】:How to optimize MATLAB bitwise operations如何优化 MATLAB 按位运算
【发布时间】:2012-07-14 09:15:40
【问题描述】:

我在 MATLAB 中编写了自己的 SHA1 实现,它提供了正确的哈希值。但是,它非常慢(在我的 Core i7-2760QM 上,一个 1000 a 的字符串需要 9.9 秒),我认为缓慢是 MATLAB 如何实现按位逻辑运算的结果(bitandbitorbitxorbitcmp)和整数的按位移位(bitshiftbitrolbitror)。

我特别想知道是否需要使用fi 命令为bitrolbitror 构造定点数字对象,因为无论如何在英特尔x86 程序集中都有rolror 用于寄存器和内存地址所有尺寸。但是,bitshift 非常快(它不需要任何定点数字结构,常规的 uint64 变量可以正常工作),这使情况变得陌生:为什么在 MATLAB 中 bitrolbitror 需要固定-指向用fi 构造的数字对象,而bitshift 没有,在汇编级别时,这一切都归结为shlshrrolror

所以,在将这个函数用 C/C++ 编写为 .mex 文件之前,我很高兴知道是否有任何方法可以提高这个函数的性能。我知道 SHA1 有一些特定的优化,但这不是问题,如果按位旋转的基本实现是如此缓慢。

tictoc 稍微测试一下,很明显是bitrolfi 中的循环导致速度变慢。有两个这样的循环:

%# Define some variables.
FFFFFFFF = uint64(hex2dec('FFFFFFFF'));

%# constants: K(1), K(2), K(3), K(4).
K(1) = uint64(hex2dec('5A827999'));
K(2) = uint64(hex2dec('6ED9EBA1'));
K(3) = uint64(hex2dec('8F1BBCDC'));
K(4) = uint64(hex2dec('CA62C1D6'));

W = uint64(zeros(1, 80));

... some other code here ...

%# First slow loop begins here.

for index = 17:80
    W(index) = uint64(bitrol(fi(bitxor(bitxor(bitxor(W(index-3), W(index-8)), W(index-14)), W(index-16)), 0, 32, 0), 1));
end

%# First slow loop ends here.

H = sha1_handle_block_struct.H;

A = H(1);
B = H(2);
C = H(3);
D = H(4);
E = H(5);

%# Second slow loop begins here.

for index = 1:80
    rotatedA = uint64(bitrol(fi(A, 0, 32, 0), 5));

    if (index <= 20)
        % alternative #1.
        xorPart = bitxor(D, (bitand(B, (bitxor(C, D)))));
        xorPart = bitand(xorPart, FFFFFFFF);
        temp = rotatedA + xorPart + E + W(index) + K(1);
    elseif ((index >= 21) && (index <= 40))
        % FIPS.
        xorPart = bitxor(bitxor(B, C), D);
        xorPart = bitand(xorPart, FFFFFFFF);
        temp = rotatedA + xorPart + E + W(index) + K(2);
    elseif ((index >= 41) && (index <= 60))
        % alternative #2.
        xorPart = bitor(bitand(B, C), bitand(D, bitxor(B, C)));
        xorPart = bitand(xorPart, FFFFFFFF);
        temp = rotatedA + xorPart + E + W(index) + K(3);
    elseif ((index >= 61) && (index <= 80))
        % FIPS.
        xorPart = bitxor(bitxor(B, C), D);
        xorPart = bitand(xorPart, FFFFFFFF);
        temp = rotatedA + xorPart + E + W(index) + K(4);
    else
        error('error in the code of sha1_handle_block.m!');
    end

temp = bitand(temp, FFFFFFFF);
E = D;
D = C;
C = uint64(bitrol(fi(B, 0, 32, 0), 30));
B = A;
A = temp;
end

%# Second slow loop ends here.

使用tictoc 进行测量,消息abc 的 SHA1 哈希的整个计算在我的笔记本电脑上花费了大约 0.63 秒,其中大约 0.23 秒在第一个慢循环中传递,大约 0.38 秒在第二个慢循环。那么在编写 .mex 文件之前,有没有办法在 MATLAB 中优化这些循环?

【问题讨论】:

    标签: performance matlab integer bit-manipulation


    【解决方案1】:

    来自 MATLAB File Exchange 的 DataHash 可以快速计算 SHA-1 哈希值。
    我运行了以下代码:

    x = 'The quick brown fox jumped over the lazy dog';  %# Just a short sentence
    y = repmat('a', [1, 1e6]);                           %# A million a's
    opt = struct('Method', 'SHA-1', 'Format', 'HEX', 'Input', 'bin');
    tic, x_hashed = DataHash(uint8(x), opt), toc
    tic, y_hashed = DataHash(uint8(y), opt), toc
    

    得到以下结果:

    x_hashed = F6513640F3045E9768B239785625CAA6A2588842
    Elapsed time is 0.029250 seconds.

    y_hashed = 34AA973CD4C4DAA4F61EEB2BDBAD27316534016F
    Elapsed time is 0.020595 seconds.

    我用random online SHA-1 tool验证了结果,计算确实是正确的。此外,106 a 的散列速度比第一句快约 1.5 倍。

    那么DataHash是怎么做到这么快的???使用java.security.MessageDigest 库,不少!
    如果您对 MATLAB 友好的快速 SHA-1 函数感兴趣,这就是您要走的路。

    但是,如果这只是实现快速位级操作的练习,那么 MATLAB 并不能真正有效地处理它们,在大多数情况下,您将不得不求助于 MEX。

    【讨论】:

    • 在我看来,加速 SHA1 的选项是使用 java.security.MessageDigest 库或编写 MEX 函数。因为我计划使我的 MATLAB 代码也与 GNU Octave 兼容(并希望也将 GNU Octave 用作开发环境),并且似乎使用 Java 库在 MATLAB 和 Octave 之间处理 Java 方面存在一些差异是一个非理想的解决方案。但是,DataHash 非常快,因此它会在我实施 MEX 解决方案或找到有效实施 SHA1 的替代方法之前完成这项工作,而无需使用 Java。
    • 将我自己的 fMRI 分析工具箱移植到 Octave 对我来说是一个长期的项目,我不想基于此限制答案。无论如何,我目前需要的是一种有效的方法来计算较大文件的 SHA1(在 MATLAB 中),以便能够继续开发,DataHash 是一个可行的解决方案。
    • @nrz:Octave 有一个兼容的 MEX API 来编写 C 扩展。他们还有自己的 API 用于编写 OCT 文件(相当于 MATLAB 中的 MEX 文件)。
    • @nrz:忘记了,Octave-Forge 上已经有一个 SHA1 函数 (SVN repo)
    • @Amro 太好了!所以解决方案是检查环境,对 MATLAB 使用DataHash,对 Octave 使用SHA1。但是,SHA1 (/usr/lib/x86_64-linux-gnu/octave/packages /general-1.3.1/x86_64-pc-linux-gnu-api-v48+/SHA1.oct) 在我的计算机上崩溃了 Octave 3.6.2,但可能很快就会修复该错误。
    【解决方案2】:

    为什么在 MATLAB 中 bittrol 和 bitror 需要用 fi 构造的定点数值对象,而 bitshift 不需要

    bitror 和 bitror 不属于适用于 uint 的按位逻辑函数集。它们是定点工具箱的一部分,其中还包含适用于定点输入的 bitand、bitshift 等变体。

    如果您想尝试仅使用 uint 函数,则可以将 bitroll 表示为两个位移位,一个 bitand 和一个 bitor。不过这可能会更慢。

    【讨论】:

    • 通过将我所有的bitrol(fi( ... 代码替换为我自己的左旋转函数 SHA1 计算时间为 1000 a 的时间从 9.9 秒下降到大约 0.42 秒,所以现在它快了大约 23.5 倍比以前。但是,计算较长消息的 SHA1 哈希(例如,FIPS 文档中的示例消息为一百万 (10^6) a's 仍然需要大约 422 秒(我的原始代码需要 9430 秒来计算),而运行 @ bash 中的 987654324@ 只需要 0.953 秒。因此,比我原来的速度快 23.5 倍,但仍比 sha1sum 慢约 440 倍。
    【解决方案3】:

    与大多数 MATLAB 函数一样,bitandbitorbitxor 是矢量化的。因此,如果你给这些函数向量输入而不是在每个元素上循环调用它们,你会得到更快的速度

    例子:

    %# create two sets of 10k random numbers
    num = 10000;
    hex = '0123456789ABCDEF';
    A = uint64(hex2dec( hex(randi(16, [num 16])) ));
    B = uint64(hex2dec( hex(randi(16, [num 16])) ));
    
    %# compare loop vs. vectorized call
    tic
    C1 = zeros(size(A), class(A));
    for i=1:numel(A)
        C1(i) = bitxor(A(i),B(i));
    end
    toc
    
    tic
    C2 = bitxor(A,B);
    toc
    
    assert(isequal(C1,C2))
    

    时间是:

    Elapsed time is 0.139034 seconds.
    Elapsed time is 0.000960 seconds.
    

    这快了一个数量级!

    问题是,据我所知,SHA-1 计算不能很好地矢量化。所以你可能无法利用这种矢量化。

    作为一个实验,我实现了一个纯基于 MATLAB 的函数来计算这样的位操作:

    function num = my_bitops(op,A,B)
        %# operation to perform: not, and, or, xor
        if ischar(op)
            op = str2func(op);
        end
    
        %# integer class: uint8, uint16, uint32, uint64
        clss = class(A);
        depth = str2double(clss(5:end));
    
        %# bit exponents
        e = 2.^(depth-1:-1:0);
    
        %# convert to binary
        b1 = logical(dec2bin(A,depth)-'0');
        if nargin == 3
            b2 = logical(dec2bin(B,depth)-'0');
        end
    
        %# perform binary operation
        if nargin < 3
            num = op(b1);
        else
            num = op(b1,b2);
        end
    
        %# convert back to integer
        num = sum(bsxfun(@times, cast(num,clss), cast(e,clss)), 2, 'native');
    end
    

    不幸的是,这在性能方面更加糟糕:

    tic, C1 = bitxor(A,B); toc
    tic, C2 = my_bitops('xor',A,B); toc
    assert(isequal(C1,C2))
    

    时间是:

    Elapsed time is 0.000984 seconds.
    Elapsed time is 0.485692 seconds.
    

    结论:写一个 MEX 函数或搜索 File Exchange 看看是否有人已经这样做了:)

    【讨论】:

    • 据我所知,SHA1 无法有效矢量化。您的my_bitops 函数似乎是一种尝试在 MATLAB 中加速计算按位运算的有趣方式,但不幸的是它并不能解决性能问题。我认为 EitanT 提到的DataHash 将是我编写 MEX 函数或使用其他人编写的 MEX 之前要走的路。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多