【问题标题】:Using vectorization instead of a 'for Loop' in Matlab在 Matlab 中使用矢量化而不是“for 循环”
【发布时间】:2017-01-19 16:35:10
【问题描述】:

我有一组来自传感器的 32 位二进制值。我必须形成这些值的所有可能组合,然后将它们转换为十进制值。

如果传入的行数超过 80000 - 90000,代码会非常慢。运行需要 120 分钟。

我想优化这段代码,因为 3 个 For 循环和最内层循环中的一个函数会降低我的算法速度。有没有机会我可以消除一些 For 循环并用矢量化代替它们以加快处理速度。

b1 = [0 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1];
b2 = [0 1 0 1 0 1 1 1 1 1 1 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1];
b3 = [0 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1];
b4 = [0 1 0 1 0 1 1 1 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1];
b5 = [0 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 1 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1];

FullVector = [b1;b2;b3;b4;b5];

for Idx = 1:size(FullVector,1)
k = 1;
MinLength = 4;             
MaxLength = 8; 
StepSize = 2;          

    for StartByte = 1:8
        for StartBit = 1:8
            for SignalLength = MinLength:StepSize:MaxLength
                DecimalVals.s(Idx,k) = BitCombinations(FullVector,StartByte,StartBit,SignalLength);
                k = k+1;
            end
        end
    end
end

功能:

function decimal = BitCombinations(ByteArray,Sbyte,Sbit,lengthSignal)
%function extracts the required bits from a byte array and 
%returns the decimal equivalent of the bits.

%Inputs:
%Sbyte  - Starting byte
%Sbit   - Starting bit in the given byte
%length - length of bits to be extracted 

%Output:
%dec    - Returns the dec  

startbit_pos = ((Sbyte-1)*8+Sbit);
endbit_pos = ((Sbyte-1)*8+Sbit+lengthSignal-1);

if endbit_pos <= 64
    extractedbits = ByteArray(startbit_pos:endbit_pos);
    extractedbits = fliplr(extractedbits);
    decimal = bi2de(extractedbits);
else
    decimal = NaN;
end

end

【问题讨论】:

  • 我认为您的示例代码不完整/工作正常。例如,Idx 不会递增。有效符号可以跨字节数组吗? (您的代码似乎允许这样做)
  • @mhopeng 很抱歉,但我认为我没有关注。我已经初始化了Idx,一切似乎都在工作。
  • 你可以在开始循环之前初始化你的 DecimalVals.s(Idx,k)
  • 我会推荐和@Dammi一样的。在这种情况下,您还可以查看是否是您机器上的内存问题。
  • 首先,不应该是... = BitCombinations(FullVector(k, :), ...吗?其次,b 的长度为 32 位,同时您检查endbit_pos 是否为&lt;64。在那种情况下不应该是&lt;32吗?

标签: matlab for-loop optimization


【解决方案1】:

您应该使用以下代码示例预先分配您的结果矩阵DecimalVals

b1 = repmat([0 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1],10000,1);
b2 = repmat([0 1 0 1 0 1 1 1 1 1 1 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1],10000,1);
b3 = repmat([0 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1],10000,1);
b4 = repmat([0 1 0 1 0 1 1 1 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 1],10000,1);
b5 = repmat([0 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 1 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1],10000,1);

FullVector = [b1;b2;b3;b4;b5];

MinLength = 4;             
MaxLength = 8; 
StepSize = 2;  

% Preallocation of the result struct
noOfValues = (((MaxLength - MinLength) / 2) + 1) * MaxLength * MaxLength;
DecimalVals = zeros(size(FullVector,1),noOfValues);

for Idx = 1:size(FullVector,1)
    k = 1;

    for StartByte = 1:MaxLength
        for StartBit = 1:MaxLength
            for SignalLength = MinLength:StepSize:MaxLength
                DecimalVals(Idx,k) = BitCombinations(FullVector,StartByte,StartBit,SignalLength);
                k = k+1;
            end
        end
    end
end

结果(在我的机器上):

没有预分配的时间消耗:560 秒

预分配时间消耗:300 秒

此外,请使用 MATLAB Profiler(使用“运行和时间”启动脚本)分别识别瓶颈,哪个函数花费的时间最多,并将函数/行添加到您的问题中。

不幸的是,我无法访问通信系统工具箱的功能,所以我使用了文件交换中的功能bi2de。在这个版本中,有一种错误检查,需要很长时间:~230 秒

【讨论】:

    【解决方案2】:

    除了预先分配数组之外,您还可以做的另一件事是不使用 Fliplr。看看这段代码sn-p

    tic
    N = 10000;
    A = [1:N];
    for i = 1:N/2
        b = A(N-i+1:-1:i);
    end
    toc
    
    b = [];
    tic
    for i = 1:N/2
        b = fliplr(A(i:N-i+1));
    end
    toc
    

    经过的时间是 0.060007 秒。 经过的时间是 0.118267 秒。

    因此,使用 Fliplr 的速度大约慢 2 倍,而不是简单地使用“向后”索引。我敢肯定,通过制作针对您的问题的自己的 bi2de 函数,您也会有所收获。

    我对此进行了尝试,但尚未检查其效率,但也许您可以将其用于您的目的

    function values = myBi2Dec(byte,signalLengths)
    
    persistent indexes
    if isempty(indexes)
        % Find the matrix indexes for this 
        indexes = [];
        for iBit = 1:8
            for iSL = signalLengths
                if iBit+iSL-1<=8
                    indexes = [indexes;sub2ind([8,8],iBit,iBit+iSL-1)];
                end
            end
        end
    end
    % Lets get the cumulative value
    cumB2D = cumBi2Dec(byte);
    
    % We already calculated their position, so simply return them
    values = cumB2D(indexes);
    
    function cumB2D = cumBi2Dec(byte)
    
    persistent B2D
    if isempty(B2D)
        B2D = zeros(8,8);
        tmp  = 2.^[0:7];
        for i = 1:8
            B2D(i,i:8) = tmp(1:8-(i-1));
        end
    end
    
    cumB2D = cumsum(repmat(fliplr(byte),8,1).*B2D,2);
    

    然后尝试,例如 myBi2Dec([0,0,0,0,1,1,1,1],[4:2:8])

    【讨论】:

      猜你喜欢
      • 2015-02-03
      • 2014-09-25
      • 1970-01-01
      • 2011-11-26
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2020-03-22
      相关资源
      最近更新 更多