【问题标题】:MATLAB Execution Time IncreasingMATLAB 执行时间增加
【发布时间】:2014-08-07 20:55:44
【问题描述】:

这是我的代码。目的是我将 Wireshark 捕获保存到一个特别格式化的文本文件中。 MATLAB 代码应该遍历数据包,针对不同的协议剖析它们,然后根据这些协议制作表格。我目前已经为 ETHERNET/IP/UDP/MODBUS 编写了这个程序。在这种情况下,它每次遇到新的寄存器值时都会在 MBTable 中创建一列,并且每次遇到对该寄存器值的更改时,它都会更新表中该行中的值。 MBTable的第一列是时间,寄存器从第二列开始。

在执行此代码之前,MBTable 已预先分配给超过 100,000 行(nol 非常大)、10 列。我拉入表中的文件的实际数据大约有 10,000 行和 4 列,代码执行速度非常慢,我不得不停止它。 tic/toc 值每 1000 行计算一次,并且随着每次迭代继续呈指数增长。这是一个很大的循环,但我看不到任何地方的增长方式会导致它在每次迭代时运行得更慢。

所有变量都在顶部初始化(省略以减少代码量。 变量 eth、eth.ip、eth.ip.udp 和 eth.ip.udp.modbus 都是 struct 类型,eth.header 和 eth.ip.header 也是如此。 WSID 是之前打开的 .txt 文件中的文件 ID。

MBTable = zeros(nol,10);
tval = tic;
while not(feof(WSID))
    packline = packline + 1;
    fl = fl + 1;

    %Get the next line from the file
    MBLine = fgetl(WSID);
    %Make sure line is not blank or short
    if length(MBLine) >= 3
        %Split the line into 1. Line no, 2. Data, 3. ASCII
        %MBAll = strsplit(MBLine,' ');
        %First line of new packet, if headers included
        if strcmp(MBLine(1:3),'No.')
            newpack = true;
            newtime = false;
            newdata = false;
            stoppack = false;
            packline = 1;
        end
        %If packet has headers, 2nd line contains timestamp
        if newpack
            Ordered = false;
            if packline == 2;
                newtime = true;
                %MBstrs = strsplit(MBAll{2},' ');
                packno = int32(str2double(MBLine(1:8)));
                t = str2double(MBLine(9:20));

                et = t - lastt;
                if lastt > 0 && et > 0
                    L = L + 1;
                    MBTable(L,1) = t;
                end
                %newpack = false;
            end
            if packline > 3              
                    dataline = int16(str2double(MBLine(1:4)));
                    packdata = strcat(packdata,MBLine(7:53));
            end
        end

    else
        %if t >= st

        if packline > 3
            stoppack = true;
            newpack = false;
        end
        if stoppack
            invalid = false;
            %eth = struct;

            eth.pack = packdata(~isspace(packdata));
            eth.length = length(eth.pack);

            %Dissect the packet data
            eth.stbyte = 1;
            eth.ebyte = eth.length;
            eth.header.stbyte = 1;
            eth.header.ebyte = 28;
            %Ethernet Packet Data
            eth.header.pack = eth.pack(eth.stbyte:eth.stbyte+27);
            eth.header.dest = eth.header.pack(eth.header.stbyte:eth.header.stbyte + 11);
            eth.header.src = eth.header.pack(eth.header.stbyte + 12:eth.header.stbyte + 23);
            eth.typecode = eth.header.pack(eth.header.stbyte + 24:eth.header.ebyte);

            if strcmp(eth.typecode,'0800')

                eth.type = 'IP';
                %eth.ip = struct;
                %IP Packet Data
                eth.ip.stbyte = eth.header.ebyte + 1;
                eth.ip.ver = eth.pack(eth.ip.stbyte);
                %IP Header length
                eth.ip.header.length = 4*int8(str2double(eth.pack(eth.ip.stbyte+1)));
                eth.ip.header.ebyte = eth.ip.stbyte + eth.ip.header.length - 1;
                %Differentiated Services Field
                eth.ip.DSF = eth.pack(eth.ip.stbyte + 2:eth.ip.stbyte + 3);
                %Total IP Packet Length
                eth.ip.length = hex2dec(eth.pack(eth.ip.stbyte+4:eth.ip.stbyte+7));
                eth.ip.ebyte = eth.ip.stbyte + max(eth.ip.length,46) - 1;
                eth.ip.pack = eth.pack(eth.ip.stbyte:eth.ip.ebyte);
                eth.ip.ID = eth.pack(eth.ip.stbyte+8:eth.ip.stbyte+11);
                eth.ip.flags = eth.pack(eth.ip.stbyte+12:eth.ip.stbyte+13);
                eth.ip.fragoff = eth.pack(eth.ip.stbyte+14:eth.ip.stbyte+15);
                %Time to Live
                eth.ip.ttl = hex2dec(eth.pack(eth.ip.stbyte+16:eth.ip.stbyte+17));
                eth.ip.typecode = eth.pack(eth.ip.stbyte+18:eth.ip.stbyte+19);
                eth.ip.checksum = eth.pack(eth.ip.stbyte+20:eth.ip.stbyte+23);

                %eth.ip.src = eth.pack(eth.ip.stbyte+24:eth.ip.stbyte+31);

                eth.ip.src = ...
                    [num2str(hex2dec(eth.pack(eth.ip.stbyte+24:eth.ip.stbyte+25))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+26:eth.ip.stbyte+27))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+28:eth.ip.stbyte+29))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+30:eth.ip.stbyte+31)))];

                eth.ip.dest = ...
                    [num2str(hex2dec(eth.pack(eth.ip.stbyte+32:eth.ip.stbyte+33))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+34:eth.ip.stbyte+35))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+36:eth.ip.stbyte+37))),'.', ...
                    num2str(hex2dec(eth.pack(eth.ip.stbyte+38:eth.ip.stbyte+39)))];

                if strcmp(eth.ip.typecode,'11')
                    eth.ip.type = 'UDP';

                    eth.ip.udp.stbyte = eth.ip.stbyte + 40;
                    eth.ip.udp.src = hex2dec(eth.pack(eth.ip.udp.stbyte:eth.ip.udp.stbyte + 3));
                    eth.ip.udp.dest = hex2dec(eth.pack(eth.ip.udp.stbyte+4:eth.ip.udp.stbyte+7));
                    eth.ip.udp.length = hex2dec(eth.pack(eth.ip.udp.stbyte+8:eth.ip.udp.stbyte+11));
                    eth.ip.udp.checksum = eth.pack(eth.ip.udp.stbyte+12:eth.ip.udp.stbyte+15);
                    eth.ip.udp.protoID = eth.pack(eth.ip.udp.stbyte+20:eth.ip.udp.stbyte+23);

                    if strcmp(eth.ip.udp.protoID,'0000')
                       eth.ip.udp.proto = 'MODBUS';

                       %eth.ip.udp.modbus = struct;
                       eth.ip.udp.modbus.stbyte = eth.ip.udp.stbyte+16;
                       eth.ip.udp.modbus.transID = eth.pack(eth.ip.udp.modbus.stbyte:eth.ip.udp.modbus.stbyte+3);
                       eth.ip.udp.modbus.protoID = eth.ip.udp.protoID;
                       eth.ip.udp.modbus.length = int16(str2double(eth.pack(eth.ip.udp.modbus.stbyte + 8:eth.ip.udp.modbus.stbyte + 11)));
                       eth.ip.udp.modbus.UID = eth.pack(eth.ip.udp.modbus.stbyte + 12:eth.ip.udp.modbus.stbyte + 13);
                       eth.ip.udp.modbus.func = hex2dec(eth.pack(eth.ip.udp.modbus.stbyte + 14:eth.ip.udp.modbus.stbyte+15));
                       eth.ip.udp.modbus.register = eth.pack(eth.ip.udp.modbus.stbyte + 16: eth.ip.udp.modbus.stbyte+19);
                       %Number of words to a register, or the number of registers
                       eth.ip.udp.modbus.words = hex2dec(eth.pack(eth.ip.udp.modbus.stbyte+20:eth.ip.udp.modbus.stbyte+23));
                       eth.ip.udp.modbus.bytes = hex2dec(eth.pack(eth.ip.udp.modbus.stbyte+24:eth.ip.udp.modbus.stbyte+25));
                       eth.ip.udp.modbus.data = eth.pack(eth.ip.udp.modbus.stbyte + 26:eth.ip.udp.modbus.stbyte + 26 + 2*eth.ip.udp.modbus.bytes - 1);


                       %If func 16 or 23, loop through data/registers and add to table
                       if eth.ip.udp.modbus.func == 16 || eth.ip.udp.modbus.func == 23
                           stp = eth.ip.udp.modbus.bytes*2/eth.ip.udp.modbus.words;
                           for n = 1:stp:eth.ip.udp.modbus.bytes*2;
                              %Check for existence of register as a key?
                              if ~isKey(MBMap,eth.ip.udp.modbus.register)
                                MBCol = MBCol + 1;
                                MBMap(eth.ip.udp.modbus.register) = MBCol; 
                              end
                              MBTable(L,MBCol) = hex2dec(eth.ip.udp.modbus.data(n:n+stp-1));
                              eth.ip.udp.modbus.register = dec2hex(hex2dec(eth.ip.udp.modbus.register)+1);
                           end
                           lastt = t;
                       end

                       %If func 4, make sure it is the response, then put
                       %data into table for register column

                    elseif false

                        %need code to handle serial to UDP conversion box

                    else
                        invalid = true;
                    end

                else
                    invalid = true;
                end

            else
                invalid = true;
            end


            if ~invalid

            end
        end
        %end
    end
   %Display Progress
   if int64(fl/1000)*1000 == fl
       for x = 1:length(mess);
           fprintf('\b');
       end
       %fprintf('Lines parsed: %i',fl);
        mess = sprintf('Lines parsed: %i / %i',fl,nol);
        fprintf('%s',mess);
       %Check execution time - getting slower:
       %%{
       ext = toc(tval);
        mess = sprintf('\nExecution Time: %f\n',ext);
        fprintf('%s',mess);
       %%}
   end
end
ext = toc - exst;

更新:我更新了上面的代码以删除重载的运算符(disp 和 lt 被替换为 mess 和 lastt)

被要求使用分析器,因此我将表中的行数限制为 2000 行(在 while 循环中添加了 && L >=2000)以限制执行时间,以下是分析器的顶级结果:

SGAS_Wireshark_Parser_v0p7_fulleth        1    57.110 s     9.714 s
Strcat                                 9187    29.271 s    13.598 s
Blanks                                 9187    15.673 s    15.673 s
Uigetfile                                 1    12.226 s     0.009 s
uitools\private\uigetputfile_helper       1    12.212 s     0.031 s
FileChooser.FileChooser>FileChooser.show    1  12.085 s      0.006s
...er>FileChooser.showPeerAndBlockMATLAB    1  12.056 s      0.001s
...nChooser>FileOpenChooser.doShowDialog    1  12.049 s    12.049 s
hex2dec                               44924     2.944 s     2.702 s
num2str                               16336     1.139 s     0.550 s
str2double                            17356     1.025 s     1.025 s
int2str                               16336     0.589 s     0.589 s
fgetl                                 17356     0.488 s     0.488 s
dec2hex                                6126     0.304 s     0.304 s
fliplr                                44924     0.242 s     0.242 s

似乎是 strcat 调用正在执行此操作。我只在一行上明确调用 strcat 。我正在做的其他一些字符串操作是间接调用 strcat 吗? 每个循环都应该调用 strcat 相同的次数,所以我仍然不明白为什么它运行的次数越多需要的时间越长......

另外,hex2dec 被调用了很多,但并没有真正影响时间。

但是无论如何,我可以使用其他方法来组合字符串吗?

【问题讨论】:

  • 注意事项 1)ltdisp 是保留字。 mathworks.com/help/matlab/ref/lt.htmlmathworks.com/help/matlab/ref/disp.html 重载肯定会减慢您的代码速度。 2)CTRL+RCTRL+T 帮助您注释/取消注释一行或选定的块(% 后面有一个空格) 3)MBTable 被初始化为nol x 10。你有可能连续写超过10 个元素吗?然后初始化失败。 4)你可以运行profiler查看哪些行比较慢mathworks.com/help/matlab/matlab_prog/…
  • 您可以使用较短的文本文件进行完整的运行,并在此处发布分析器结果。消耗大量时间的行最感兴趣。
  • 感谢您的提示,伊冯。删除重载的运算符使其启动速度更快,但每次迭代仍然会减慢很多。如上所述,我正在测试的文件仅写入 10 列中的 4 列,所以这不是问题。我将按照您的建议尝试分析器...
  • 我运行了配置文件并将结果发布在我上面的问题中。我仍然不确定该怎么做。
  • mathworks.com/matlabcentral/newsreader/view_thread/292870 strcat 据报道速度很慢。你能把strcat(packdata,MBLine(7:53));改成sprintf('%s%s', packdata, MBLine(7:53));看看时间变化吗?如果出现任何错误,请更正代码,因为我不确定您在这两个变量中拥有的实际数据。

标签: matlab optimization


【解决方案1】:

问题来了:

字符串(MATLAB 中的 char 数组)packdata 被一遍又一遍地调整大小和重新分配。这就是减慢此代码的原因。我做了以下步骤:

我消除了多余的变量 packdata,现在只使用 eth.pack。

我预先分配了 eth.pack 和几个已知长度的“辅助变量”,方法是在循环开始之前为每个变量运行一次空白

eth.pack = blanks(604);
thisline = blanks(47);
smline = blanks(32);

(注:604是基于headers + MODBUS协议的packdata的最大可能大小)

然后我创建了一个指针变量来指向最后一个写入packdata的字符的位置。

pptr = 1;

...

dataline = int16(str2double(MBLine(1:4)));

thisline = MBLine(7:53); %Always 47 characters

smline = [thisline(~isspace(thisline)),blanks(32-sum(~isspace(thisline)))]; %Always 32 Characters

eth.pack(pptr:pptr+31) = smline;

pptr = pptr + 32;

上面是在'if packline > 3'块内代替'packdata ='语句,然后在'if stoppack'块的末尾是reset语句:

pptr = 1; %Reset Pointer

仅供参考,这并不奇怪这会在我的代码中带来其他缺陷,这些缺陷我已经大部分修复但仍需要完成。现在这不是一个大问题,因为这个循环会随着这些变化快速执行。感谢 Yvon 帮助我指明了正确的方向。

我一直在想我的大表,MBTable 是问题...但它与它无关。

【讨论】:

    猜你喜欢
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多