Many posters already suggested good answers. I am just adding some explanation behind this behavior. Also, I have to warn that this is my understanding, and I couldn't find any reference. So, please take it with grain of salt. I am more than happy to stand corrected, if found wrong.
Problem
It is a rendering issue. And it is in fact Windows' GDI+ related issue (and probably Apple too). I tried my best, but couldn't find related Microsoft KB (which I know there is...).
Anyway, this is what is roughly happening. If you have lines defined by Line[{pt1, pt2, ...}]
, system graphics API treat it as a single path (with multiple segment). Now, in a single path case, the API, especially the rasterizer is trying to see whether each pixel is intersected by multiple segments. The reason? Because of the opacity. Compare these two images (very exaggerated, but the idea is there).
pts = {{0, 0}, {1, 1}, {1, 0}, {0, 1}};
{Graphics[{Thickness[.1], Opacity[.3], Line[{{0, 0}, {1, 1}, {1, 0}, {0, 1}}]}],
Graphics[{Thickness[.1], Opacity[.3], Line[Partition[pts, 2, 1]]}]}
If you don't treat the self-intersection, what you end up getting is something like the second result (transparency accumulated in self-intersection points). Which is very bad for many cases. Now it shouldn't be a problem when opacity is 1, but unfortunately if you use anti-aliasing, then you are in effect introducing a different opacity, this should be dealt. This is no problem with multi-path case (like the second image), since in that case, you can just accumulate pixel values at each pixel.
To prove it, compare the following point configuration and speed difference:
The same number of points, but the rendering of the second one is much faster.
Now, back to our problem. When we are using ListLinePlot
without any options (or just PlotRange->All
), the process is very close to what Leonid's myListPlot
is doing. Which means that it will generate a long chain of Line[{pt1, pt2, ...}]
and the self-intersection routine will kick in. If you see the original result closely, you will see that in fact, the resulting graphics has a lot of self-intersection in pixel-res space (it does computation on pixel-level).
Solutions
In fact, Mr.Thomas already got his own answers.
Turing off anti-aliasing (by Style[..., AntiAliasing->False]
). This way, the rasterizer spend a lot less time processing intersections, thus much much faster.
Sub-sampling. There are multiple ways, MaxPlotPoints
is one, Thomas' code is another, and much more.
Cheating by making multiple paths:
Well, this solution isn't going to give you super boost. Just in case you absolutely need anti-aliasing, and also need to use all the points. Look at a part of my first example:
Line[Partition[pts, 2, 1]]
You will see that we are essentially turning Line[{pt1, pt2, pt3, pt4, ...}]
into Line[{{{pt1, pt2}}, {{pt2, pt3}}, {{pt3, pt4}}, ...}]
. As long as the opacity remains 1, the result should be about the same (except JoinedForm
, but in our case it is no concern). The benefit of the second form is that now it is not considered to be a single path. So, the rasterizer will think that it is multiple-path, and no self-intersection treatment.
I modified Lenoid's myListPlot
a bit so that it does this task.
ClearAll[myListPlot2];
Options[myListPlot2] = {AspectRatio -> GoldenRatio^(-1), Axes -> True,
AxesOrigin -> {0, 0}, PlotRange -> {All, All},
PlotRangeClipping -> True,
PlotRangePadding -> {Automatic, Automatic}};
myListPlot2[pts_List, opts : OptionsPattern[]] :=
Module[{pts1, pts2},
pts1 = Developer`ToPackedArray@
Transpose[{N@Range[Length[pts]], pts}];
pts2 = Partition[pts1, 2, 1];
Graphics[{{{}, {}, {Hue[0.67, 0.6, 0.6], Line[pts2]}}},
FilterRules[{opts, Options[myListPlot]}, Options[Graphics]]]]
Now, time to prove the theory. With the same data set rv
, here is the timing comparison between myListPlot
and myListPlot2
(myListPlot
is faster than ListLinePlot
so it is enough).
Well, about twice as fast. Frankly, it is not that practical, just theoretical interest :)
Addendum
Szabolcs found some bundling going on at 500 (Not sure it is Windows specific or number is exactly that, but bundling is true), which makes me experimenting with different segmenting--instead of just 2, partitioning it at 3, 4, ... etc. And I got all different speed result (getting faster, then slower again). If you use Partition
it drops the tail (and I personally am still learning all complex syntax, so...), but it shouldn't make that much of difference, in my opinion.
So, the problem is probably not just affected by the self-intersection, but also the bundling. It gets more interesting / complex :)
Timing
orAbsoluteTiming
to time the execution of a command, instead of having to subtract time values manually. $\endgroup$Graphics
object to render on screen?AbsoluteTiming
might cover that as well, since it measures wall time, not CPU time, though I forget when exactly it stops. (BTW, if rendering is the bottleneck, saving the plot to a rasterized image file can be a good strategy) $\endgroup$AbsoluteTiming[]
doesn't cover rendering of the graphic. It only times how long it takes for the kernel to finish, and the rendering is done by the front end only after theGraphics
expression has been sent to it. Generally, bothTiming
andAbsoluteTiming
measure the time it takes for the kernel to finish (not front end), but the former is CPU time while the latter is wall time. $\endgroup$AbsoluteTiming
won't measure graphics rendering time (because I tried it in the past, though not with v8). $\endgroup$