How to dramatically speed-up the function plot

246 visualizzazioni (ultimi 30 giorni)
Introduction
Recently I found that in some circumstances the function plot is extremely slow, and I needed to find a solution to this problem. This post contains a description of this problem, and a possible solution which I found.
More specifically, consider the following code:
N = 1E6;
x = rand(N,1) ; y = rand(N,1);
figure
subplot(1, 3, 1);
tic ; hndl = plot(x, y, 'k--') ; axis square ; toc
numel(hndl)
subplot(1, 3, 2);
x_ = [ x(1:end-1) x(2:end) ]';
y_ = [ y(1:end-1) y(2:end) ]';
tic ; hndl = plot(x_, y_, 'k--') ; axis square ; toc
numel(hndl)
This program plots exactly the same image, with exactly the same number of lines, but the computational time are astonishingly different! On my PC I got the following result:
Elapsed time is 0.003680 seconds.
ans =
1
Elapsed time is 313.721635 seconds.
ans =
999999
Possible explaination for this issue
This issue is by no way related with a problem with OpenGL, or the graphic card driver, or the operative system. The point is that plotting a single polygon composed of N line by using the function plot is extremely faster than plotting the same N lines independently. In fact, I believe that the problem is due to the fact that when multiple lines have to be plotted, Matlab must creates as many instances of graphical objects (in this case matlab.graphics.chart.primitive.Line). Of course, this is my personal opinion.
By the way, replacing the function plot by the function line, or increasing Java memory both doesn't improve the performances.
Solution
A possible way to mitigate this problem is to use the function patch instead of plot. In most cases, this will produce nearly identical outputs. For instance, we can complete the previous program with:
subplot(1, 3, 3);
x_ = [ x(1:end-1) x(2:end) ]';
y_ = [ y(1:end-1) y(2:end) ]';
tic ; hndl = patch(x_, y_, 'k') ; axis square ; box on ; toc
numel(hndl)
One observes that in this case a single handle of class matlab.graphics.primitive.Patch is instanciated. The computational time is still much higher than in the trivial case (plotting of a polygon, elaped time = 0.003680 seconds), but becomes acceptable:
Elapsed time is 0.688164 seconds.
ans =
1
Hereafter it is depicted the output of the program in the case N = 20. One observes that the three graphics are practically identical:
This workaround is acceptable when continuous lines have to be plotted. As long as it is required to have more styled lines, it is possible to play with the argument provided to the function patch, but the result will not be exactly the same. For instance, in order to obtain dashed lines one can se the property LineStyle:
hndl = patch(x_, y_, 'k', 'LineStyle', '--')
The output is not identical to the one obtained with plot, but it may be acceptable, depending on your requirements:
Conclusion
In some circumstances the function plot has very poor performances. This issue seems to be related to the number of instances of graphical handles which are created. A possible workaround, which produces identical or very similar outputs, is to replace the function plot with patch.
R. Scorretti

Risposta accettata

Steven Lord
Steven Lord il 5 Giu 2020
The key information is in the Description section on the plot documentation page.
"plot(X,Y) creates a 2-D line plot of the data in Y versus the corresponding values in X.
  • If X and Y are both vectors, then they must have equal length. The plot function plots Y versus X.
  • If X and Y are both matrices, then they must have equal size. The plot function plots columns of Y versus columns of X."
Let me show a smaller data set, N = 100.
>> N = 1e2;
>> x = rand(N,1) ; y = rand(N,1);
>> x_ = [ x(1:end-1) x(2:end) ]';
y_ = [ y(1:end-1) y(2:end) ]';
>> whos x x_ y y_
Name Size Bytes Class Attributes
x 100x1 800 double
x_ 2x99 1584 double
y 100x1 800 double
y_ 2x99 1584 double
Plotting x and y falls into the first case, since they're both vectors. You get one graphics object.
Plotting x_ and y_ falls into the second case, since they're both matrices. You get size(x_, 2)-1 (or writing it a different way, N-1) graphics objects.
If you want to plot the individual line segments visually but create only 1 graphics object instead of N-1, you can do this by adding NaN values.
>> x2 = [x_; NaN(1, N-1)];
>> x2 = x2(:);
>> y2 = [y_; NaN(1, N-1)];
>> y2 = y2(:);
Let's try this on some non-random data.
>> A = [1 3 5; 2 4 6; NaN(1, 3)];
>> A = A(:);
>> B = A;
>> h = plot(A, B);
>> numel(h) % 1
Visually there are three line segments. The points (2, 2) and (3, 3) aren't connected because there's a (NaN, NaN) inbetween. But h only has one element.
  1 Commento
Riccardo Scorretti
Riccardo Scorretti il 5 Giu 2020
Modificato: Riccardo Scorretti il 5 Giu 2020
Thank you very much, that works fine!
In order to complete the answer, I report hereafter the modification which applies in the case of the original example:
x_ = [ x(1:end-1) x(2:end) ]' ; x_(end+1,:) = NaN;
y_ = [ y(1:end-1) y(2:end) ]' ; y_(end+1,:) = NaN;
tic ; hndl = plot(x_(:), y_(:), 'k--') ; axis square ; toc
numel(hndl)

Accedi per commentare.

Più risposte (1)

Ameer Hamza
Ameer Hamza il 5 Giu 2020
Modificato: Ameer Hamza il 5 Giu 2020
I am not sure what is the purpose of using second code is instead of the first when both create the same plots, however, if you want to use the second method, the following will be faster
subplot(1, 3, 2);
x_ = [ x(1:end-1) x(2:end) ]; % <== remove transpose
y_ = [ y(1:end-1) y(2:end) ]; % <== remove transpose
tic ; hndl = plot(x_, y_, 'k--') ; axis square ; toc
numel(hndl)
The reason for it being slow is that MATLAB needs to create multiple line objects (equal to number of columns in x_) in that case, which is a slow operation. In the first case, only a single line object is created.
  2 Commenti
Riccardo Scorretti
Riccardo Scorretti il 5 Giu 2020
Modificato: Riccardo Scorretti il 5 Giu 2020
I'm afraid that the solution you propose works in the particular case of my example because the graphic is a single polygonal line, but unfortunately it doesn't work in a more general case.
In fact, I'm working with a Finite Element program, and I need to plot a large number of disconnected lines (= which don't form a single polygonal line). Here is what I get when I simply remove the transpose in a real case:
Of course the expected output is on the right, and it has been obtained by replacing plot by patch.
Ameer Hamza
Ameer Hamza il 5 Giu 2020
Yes, this can happen, because the two methods in your question are not identical, so the comparison might not be useful. I guess the simple workaround is to use patch(), a more complicated (but maybe more accurate) solution might be to rearrange the vertices such that a single connected line can trace this shape (not sure if it is feasible).

Accedi per commentare.

Prodotti


Release

R2019b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by