【问题标题】:Slowing Speed with each for loop iteration in matlabmatlab中每个for循环迭代都会减慢速度
【发布时间】:2016-07-28 15:20:22
【问题描述】:

我在 Matlab 中编写了一个 while 循环,它应该使用 Matlab 中的 tic toc 延迟以指定的时间间隔将数组中的每个值从 Matlab 发送到 arduino,然后读取值并将它们存储在变量中并绘制它们.

while 循环的输出会随着每次连续迭代而减慢。

我增加了缓冲区大小,这对它有很大帮助,但它仍然减慢了太多。是否有另一种方法可以提高按时打印值的速度。我已经包含了另一个 tic toc 和图表来显示这里的执行速度是代码:

max = 80; min = 40; amp = (max-min)/2; offset = amp + min; btime = 5; bpm = 12; spb = 60/bpm; sapb = spb/.05; tosd = sapb*bpm*btime; time1 = btime*60; x = linspace(0,time1,tosd)'; x1 = amp*sin(x*(2*pi/20)) + offset; pause(1); fprintf(handles.UltraM,(['<P' num2str(offset) '>'])) pause(5); y = []; i = 1; figure(1); hold on; title('Pressure Data'); xlabel('Data Number'); ylabel('Analog Voltage (0-1023)'); t1 = []; figure(2); hold on; title('Time to execute task'); xlabel('iteration number'); ylabel('time taken'); while (i<=length(x)) t2 = tic; t = tic; fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>'])); %disp((['<P' num2str(x1(i)) '>'])); y(i) = fscanf(handles.UltraM,'%d'); figure(1); hold on; plot(i, y(i), 'b*'); drawnow; hold off; while toc(t) < 0.05 continue end t1(i) = toc(t2); figure(2); hold on; plot(i,t1(i),'b*'); drawnow; hold off; i = i + 1; end

【问题讨论】:

  • 我不确定您看到了什么问题,但是您可以将for 循环缩短为:for i=1:length(x), disp(['&lt;P' num2str(x1(i)) '&gt;']); pause(.05); end,这样可以节省一些作业并使其更具可读性。请注意,在此更改之后,您也不再需要d。此外,我试图重现这一点,但在所有迭代中我看不到 for 循环的运行时间(如我的建议)的任何变化。
  • 刚刚写了一个答案,但意识到它可能为时过早。由于您的循环中没有不断增长的向量,因此它不应该增加运行时间。无论如何,如果有帮助,您可以在发送字符串之前对其进行预处理,然后对cellstr 的每个引用将是O(1)
  • 我无法在我的机器上重现此问题。我试图给它计时,但一部分来自一些随机尖峰,这是预期的,因为时间似乎保持在 0.05 秒以上。但是,我想指出的是,您有很多常量的含义相同或部分相同。这使得很难看出它们之间的关系。例如,很难看出tosd 实际上独立于bpm。为避免掩盖此类行为,您需要清楚自己的意图。通过代码或通过 cmets。
  • handles.UltraM 未在您的代码中定义。此外,您似乎打开了重复的question。只是提醒一下,这是对 SO 的一大禁忌,最终无助于解决您的问题。
  • 我离开了串口的初始化和参数我不想包含一堆对我试图解决的问题并不重要的代码@Alexander F ...在我重新提出这个问题之前,我打开了第二个问题,因为他们讨论了不同的主题,我已经删除了它

标签: matlab for-loop serial-communication


【解决方案1】:

经过一番折腾,我想我知道您想要实现什么目标以及阻碍您实现的目标。

我已经编辑了您的代码,使其更加快速和易读。大多数情况下,操作所花费的时间略高于0.05 秒,并且在几个时间点可能需要比预期长约5 毫秒。当然,您的米数可能会有所不同。因为我没有arduino,所以我不知道那里是否有瓶颈。您还应该尝试使用内置的 Matlab 分析器(它非常有用)来分析您的代码,以查看究竟是什么减慢了您的代码。

我发现使您的代码变慢的主要原因是您使用plot 函数一次将一个点添加到您的图形中。每次调用此函数时,它都会创建一个新的图形对象。在几百次之后,事情变得迟缓。相反,您应该简单地更新已经绘制的数据并使用drawnow 重新绘制它。

简而言之,解决方案是这样的:

1) 用一个点初始化你的绘图并保存图形句柄供以后使用:

p1 = plot(0,0,'b*');

2) 然后,在循环内部,一旦您的数据数组被更新,用新数组替换现有绘图中的数据。

set(p1, 'XData', 1:i, 'YData', y(1:i));

3) 重新绘制绘图以反映最新更新。

drawnow;

drawnow 最终也会减慢您的代码速度,因为它必须在每次迭代时重新绘制越来越大的图。为了使事情更快地进行,您可能希望在较长的时间间隔后刷新您的绘图。例如,以下将每 10 次迭代刷新一次:

if rem(i,10) == 0
    drawnow;
end

下面的完整代码。如果您还有其他问题,请告诉我。

max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
%fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
disp(['<P' num2str(offset) '>']); % replacing with disp (I don't have an arduino)
pause(5);
%y = []; % unnecessary here, preallocated before loop
figure(1); 
p1 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
%t1 = []; % unnecessary here, preallocated before loop
figure(2);
p2 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');

% preallocate t1 and y arrays for faster operation
t1 = zeros(size(x));
y  = zeros(size(x));
i = 1; % moved closer to loop beginning for better readability
while i <= length(x) % parentheses unnecessary in Matlab
    t2 = tic;
    t = tic;
    %fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
    disp((['<P' num2str(x1(i)) '>'])); % replacing with disp (I don't have an arduino)
    %y(i) = fscanf(handles.UltraM,'%d');
    y(i) = randn; % replacing with random number (I don't have an arduino)
    %figure(1); % unnecessary
    %hold on; % unnecessary
    %plot(i, y(i), 'b*');
    % replacing the above with a slightly faster version
    set(p1, 'XData', 1:i, 'YData', y(1:i));
    %drawnow; % first one is annecessary
    %hold off; % unnecessary
    while toc(t) < 0.05
        continue
    end
    t1(i) = toc(t2);
    %figure(2); % unnecessary
    %hold on; % unnecessary
    %plot(i,t1(i),'b*');
    % replacing the above with a slightly faster version
    set(p2, 'XData', 1:i, 'YData', t1(1:i));
    if rem(i,10) == 0 % refreshing every 10 iterations
        drawnow; 
    end
    %hold off; % unnecessary
    i = i + 1;
end

回答以前版本的问题

您可以通过将循环完全替换为以下两个语句来矢量化循环:

% vectorizing num-to-string conversion
y4 = cellstr(strcat('<P',num2str(x1), '>'));

% deleting all spaces
y4 = cellfun(@(u) u(~isspace(u)), y4, 'UniformOutput', false)

这个小调整使您的程序在我的电脑上运行x4 更快。

也可以使用 cellfun 迭代器来显示/打印结果:cellfun(@disp, y4)

【讨论】:

  • 问题是我需要一次发送一个值,因此索引 1 索引 2 等,我需要以某个统一的间隔发送它们我尝试修改您提供的代码,以便它会这样做但性能仍然很慢,当我尝试运行它 5 分钟时,它比它应该花费的时间长了大约 1 分钟
  • @emg184,我很困惑你所说的“发送值”是什么意思。此外,这些值是否需要即时生成,或者可以像我建议的那样进行预处理?在生成字符串后,您仍然可以有一个循环来一次打印一个值。在我不太热的笔记本电脑上生成只需不到一秒钟。您是否尝试过分析您的代码以查看在运行时花费最多的时间?如果是 disp 函数,你可以尝试使用 fprintf 代替,应该会稍微快一些。最后,你为什么pause(0.5)
  • @emg184,如果您的目标是每0.05 秒打印一个字符串,则使用pause 将不准确。相反,您应该使用以下内容(假设您按照我的建议预处理了字符串):for i=1:d, t = tic; fprintf('%s\n', y4{i}), while toc(t) &lt; 0.05, continue; end, end。很抱歉单行,cmets 不允许缩进。如果这是你需要的,我会写在我的答案中。在这种情况下,您甚至不需要预处理。
  • 感谢您的回复抱歉等待回复您我离开了一会儿。我按照你说的做了,它的性能更好,但是我想做的是用 fprintf 将值发送到 arduino,同时用 fscanf 读取从 arduino 传入的数据,然后实时绘制它我已经打印了但是,值部分得到了处理,我可以读取这些值,但我似乎无法将它们存储在数组中以便稍后绘制。我可以实时绘图,但是当我实时绘图时,它不允许我正确读取数据。
  • @emg184,阅读输入是您在问题或示例代码中没有提到的全新问题。也许您应该重新提出问题以适应您想要达到的目标以及在哪些限制条件下。
猜你喜欢
  • 2011-08-16
  • 2012-03-03
  • 1970-01-01
  • 2019-02-20
  • 2021-10-14
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多