On Tuesday, October 22, 2013 06:15:45 AM Joonas Nättilä wrote:
>While it is great to have many
> options, the large number of different system can also be a drawback for
> users not familiar with all the different backends. Due to this, I think we
> should focus on one system and advertise this everywhere.
I like this idea overall, but it's difficult when all existing options have
strengths and weaknesses. I doubt we even have the core graphics
infrastructure right, let alone any consensus on a favorite plotting
interface. I suspect we should actively promote the success of both Gadfly and
Winston, at a minimum, and be grateful for the rest as "stopgaps" in the
meantime.
This morning I did some very simple benchmarking, since I think performance
will matter a lot. The test case: plotting a line, without markers, of the
form
x = 1:10^5
y = rand(length(x))
I don't think of this as particularly large (we should darn-well be able to do
even bigger datasets), but it's big enough to push the system. Nothing
performed flawlessly. The test was purely qualitative, and all I evaluated was
(1) how quickly the graph appeared, and (2) the experience in resizing the
window. Note this isn't really testing _plotting_, it's testing _graphics_.
Important detail: on my system the native Cairo library suffered from the issue
here:
https://groups.google.com/d/msg/julia-users/oNPhJ9iHIDY/CP6oBgPq2SQJ
and so this was done with a hand-built version that performed better:
https://groups.google.com/d/msg/julia-users/oNPhJ9iHIDY/GpV7SJUgO50J
Results, very qualitative, on my wimpy laptop:
- Matlab (for comparison): window pops up in ~0.7 seconds. Resize behavior is
"interesting:" it's fluid to drag a corner of the window to resize it, but the
plot only updates when you let go. While this is slightly odd, on balance it
generated the second-most pleasant behavior I experienced (and would have been
the most pleasant if I had chosen 10^6 points rather than 10^5).
- Winston, with either the Tk or Gtk backend (I couldn't tell any difference):
roughly 1 second to show the graph on the screen (definitely slower than
Matlab, but not ridiculously so). Resize performance is a little frustrating
but tolerable. It doesn't use the Matlab trick, so the window shape jerks from
one to the next between ~1s updates.
- Gadfly, rendering to an SVG file (I couldn't get the D3->browser interface
https://github.com/dcjones/Gadfly.jl#using-the-d3-backend
to work for me, otherwise the results might have been different): about 2.5s to
write the file. Not sure whether viewer time is relevant, but the one I used
(Gwenview) had seconds-long rendering times.
- Gadfly, through IJulia: completely unusable. Gadfly finished its job quickly
(_well_ under a second, didn't time it because it was fast), but on my system
chrome was running 1 CPU at 100% for the next 60 seconds or more before finally
rendering the image. Scrolling the browser window then became impossible. Even
10^4 points takes several seconds.
- Gaston: gaston finishes its job quickly (~0.1s), but the window doesn't pop
up in gnuplot for another 3-4 seconds. Resizing the window seems basically
impossible.
- PyPlot: quite similar to Winston. Slightly worse resize performance, but
nothing too dramatic.
I also tested another contender, Qwt, modifying the "curvdemo1" example (code
below). Even including the time to launch from a bash prompt and generate the
random numbers (something that wasn't in any of the other tests), it was by
far the fastest to render (< 0.25 seconds) and had by far the most pleasant
resize experience (although you could perceive some lag, which grew quite bad
when I increased the number of points to 10^6, at which point it was less
pleasant than Matlab to resize the window).
Overall, with the possible exception of Qwt (and Matlab :) ) it's not exactly
a pretty picture. Personally I see a few ways forward (again, I'm just talking
graphics, not plotting):
- Live with Cairo's current performance, eventually adopting Gtk over Tk. This
would not be a disaster, but probably doesn't qualify as awesome.
- Figure out why Cairo's performance stinks compared to Qwt/Qt's, and fix it.
- Experiment with Cairogles:
https://github.com/SRA-SiliconValley/cairogles
- See whether D3 coupled with Gadfly writing its results to a ramdisk like
/dev/shm fixes things. Big possible problem here (others will have to inform
me): can we build GUIs and get interactivity, with _julia_ function callbacks,
this way?
- Adopt Qt and possibly Qwt (huge negative: a massive number of callback
functions would currently be required)
- Adopt QML,
http://qt-project.org/doc/qt-5.1/qtdoc/qtquick-
applicationdevelopers.html, a sort of javascript-like scene description
language (it's a declarative, not imperative, way of setting up GUIs). This
would require some wrappers (but perhaps far fewer than wrapping classic Qt),
would mean that we'd write GUI "button logic" in QML, and perhaps we'd use
jlapi.c to create julia callbacks (I _think_ we could set up a strategy to
avoid the need for end-users to compile anything, but I'm not certain). There
are some scary steps in adopting this route because there's a lot uncertain
and untested (I suspect this would qualify as "bleeding edge"), and some
serious potential negatives for remote-server operation (such as likely
requiring VirtualGL, gack). On the plus side, I've played a bit with its
graphics over the last few days; suffice it to say that if you want rather
amazing eye candy with impressive performance, this route appears to have a
few advantages :-).
Not sure what to do here. I wonder if Cairogles might be the best choice
(although it too might require VirtualGL, unless it has a non-OpenGL
fallback).
Best,
--Tim
Qwt demo (place in the curvdemo1 directory; tested on 6.0, since that's what I
had pre-installed):
#include <qwt_scale_map.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_math.h>
#include <qcolor.h>
#include <qpainter.h>
#include <qapplication.h>
#include <qframe.h>
#include <stdlib.h>
//------------------------------------------------------------
// curvdemo1
//
// This example program features some of the different
// display styles of the QwtPlotCurve class
//------------------------------------------------------------
//
// Array Sizes
//
const int Size = 100000;
const int CurvCnt = 1;
//
// Arrays holding the values
//
double xval[Size];
double yval[Size];
QwtScaleMap xMap;
QwtScaleMap yMap;
class MainWin : public QFrame
{
public:
MainWin();
protected:
virtual void paintEvent( QPaintEvent * );
void drawContents( QPainter *p );
private:
void shiftDown( QRect &rect, int offset ) const;
QwtPlotCurve d_curves[CurvCnt];
};
MainWin::MainWin()
{
int i;
xMap.setScaleInterval( -0.5, 10.5 );
yMap.setScaleInterval( -1.1, 1.1 );
//
// Frame style
//
setFrameStyle( QFrame::Box | QFrame::Raised );
setLineWidth( 2 );
setMidLineWidth( 3 );
//
// Calculate values
//
for( i = 0; i < Size; i++ )
{
xval[i] = double( i ) * 10.0 / double( Size - 1 );
//yval[i] = qSin( xval[i] ) * qCos( 2.0 * xval[i] );
yval[i] = drand48();
}
//
// define curve styles
//
i = 0;
d_curves[i].setPen( QColor( Qt::darkBlue ) );
d_curves[i].setStyle( QwtPlotCurve::Lines );
d_curves[i].setRenderHint( QwtPlotItem::RenderAntialiased );
i++;
//
// attach data
//
for( i = 0; i < CurvCnt; i++ )
d_curves[i].setRawSamples( xval, yval, Size );
}
void MainWin::shiftDown( QRect &rect, int offset ) const
{
rect.translate( 0, offset );
}
void MainWin::paintEvent( QPaintEvent *event )
{
QFrame::paintEvent( event );
QPainter painter( this );
painter.setClipRect( contentsRect() );
drawContents( &painter );
}
//
// REDRAW CONTENTS
//
void MainWin::drawContents( QPainter *painter )
{
int deltay, i;
QRect r = contentsRect();
deltay = r.height() / CurvCnt - 1;
r.setHeight( deltay );
//
// draw curves
//
for ( i = 0; i < CurvCnt; i++ )
{
xMap.setPaintInterval( r.left(), r.right() );
yMap.setPaintInterval( r.top(), r.bottom() );
painter->setRenderHint( QPainter::Antialiasing,
d_curves[i].testRenderHint( QwtPlotItem::RenderAntialiased ) );
d_curves[i].draw( painter, xMap, yMap, r );
}
//
// draw titles
//
r = contentsRect(); // reset r
painter->setFont( QFont( "Helvetica", 8 ) );
const int alignment = Qt::AlignTop | Qt::AlignHCenter;
painter->setPen( Qt::black );
painter->drawText( 0 , r.top(), r.width(), painter-
>fontMetrics().height(),
alignment, "Style: Lines, Symbol: None" );
}
int main ( int argc, char **argv )
{
QApplication a( argc, argv );
MainWin w;
w.resize( 300, 600 );
w.show();
return a.exec();
}