Java2D vs. Cairo stroking shootout

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi, 
with all the talk of AWT performance and some talk about caching
stroking, I did some benchmarking. (Attached)

Basically, we suck :) All tests run on FC5 on an x86.
Java 1.5.0: 0.47 seconds
Java 1.4.2: 0.40 seconds (consistently faster!)

JamVM/Classpath/Cairo 1.0.4: 7.9 seconds (ugh)

Now, having implemented all the speedup ideas I have for this,
(caching of path objects), the speedup was negligible[1].

The reason is simple, measuring the time spent in cairo_stroke() only,
(benchmark not included, this was hacked into the C code of our
 CairoGraphics2D), we find that way over 95%, of the time here is spent
in cairo_stroke(). And since cairo_fill() doesn't work significantly
faster, my conclusion is that there's no workaround for a slow Cairo and
there's no point in us trying to speed this up much more at the moment.

Some results:
Drawing to a Cairo surface (instead of an xlib one) was 
somewhat faster, around 5.8 seconds. Still very slow.

Cairo 1.2 seemed a tad faster for cairo surface, about 5.7 s,
for xlib surfaces there was no significant difference.

[1] The things I tried were: 
* Caching PathIterator objects in the Shape (the benchmark uses
GeneralPath, which does not cache its PIs in the current
version). 
* Caching the cairo_path_t:s of various object, avoiding iterating over
the path and calling into cairo every time.
* Stroking the path ourselves with BasicStroke and and using
cairo_fill() instead. 
* Caching the above.

Again, none of this yields any significant speedups.

So basically, let's go bug the Cairo guys. :)
Supposedly they're going to work on making stuff faster now, so
it'll be interesting to revisit these results later.

/Sven

import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.geom.GeneralPath;
import java.util.Random;

public class StrokeBench extends Frame
{
  private static final int NPATHS = 500;
  private static final int MAXSEGMENTS = 30;
  private static final long SEED = 1294;
  private static final int WINSIZE = 500;

  private static final int NITERATIONS = 10;

  private GeneralPath[] paths;
  private long totalTime;
  private double iterations;
  private Random r;

  private StrokeBench()
  {
    super("Stroke benchmark");
    r = new Random(SEED);
    generatePaths();
    // Random ID # to confirm we've used the same numbers.
    System.out.println("ID: "+r.nextInt()); 
    setSize(WINSIZE, WINSIZE);
    totalTime = 0;
    setVisible(true);
  }

  private void generatePaths()
  {
    paths = new GeneralPath[ NPATHS ];
    for(int i = 0; i < NPATHS; i++ )
      {
	paths[i] = new GeneralPath();
	int nSegs = r.nextInt(MAXSEGMENTS);
	paths[i].moveTo((float)(r.nextFloat() * WINSIZE),
			(float)(r.nextFloat() * WINSIZE));
	for(int j = 0; j < nSegs; j++ )
	  addRandomSegment( paths[i] );
	if( r.nextBoolean() ) 
	  paths[i].closePath();
      }
  }

  private void addRandomSegment( GeneralPath path )
  {
    int type = r.nextInt(3);

    switch(type)
      {
      case 0:
	path.lineTo((float)(r.nextFloat() * WINSIZE),
		    (float)(r.nextFloat() * WINSIZE));
	break;
      case 1:
	path.quadTo((float)(r.nextFloat() * WINSIZE),
		    (float)(r.nextFloat() * WINSIZE),
		    (float)(r.nextFloat() * WINSIZE),
		    (float)(r.nextFloat() * WINSIZE));
	break;
      case 2:
	path.curveTo((float)(r.nextFloat() * WINSIZE),
		     (float)(r.nextFloat() * WINSIZE),
		     (float)(r.nextFloat() * WINSIZE),
		     (float)(r.nextFloat() * WINSIZE),
		     (float)(r.nextFloat() * WINSIZE),
		     (float)(r.nextFloat() * WINSIZE));
	break;
      }
  }

  public void paint(Graphics gr)
  {
    Graphics2D g = (Graphics2D)gr;
    g.setColor(Color.white);
    g.fillRect(0, 0, WINSIZE, WINSIZE);
    g.setColor(Color.black);
    long start = System.currentTimeMillis(); 
    for( int i = 0; i < NPATHS; i++ )
      g.draw( paths[ i ] );
    long time = System.currentTimeMillis() - start;
    totalTime += time;
    iterations += 1.0;
    System.out.println("Time: "+time+" ms\tTotal time: "
		       +totalTime+" ms\t Average: "+
		       (((double)totalTime)/(1000*iterations))+" s");
    if( iterations > NITERATIONS )
      System.exit(0);
    repaint(); // Keep triggering repaints, quite nasty :)
  }

  public static void main(String[] args) 
  {
    new StrokeBench();
  }
}

[Index of Archives]     [Linux Kernel]     [Linux Cryptography]     [Fedora]     [Fedora Directory]     [Red Hat Development]

  Powered by Linux