Re: Speed of Quartz (was: optimizing compilers)
Re: Speed of Quartz (was: optimizing compilers)
- Subject: Re: Speed of Quartz (was: optimizing compilers)
- From: email@hidden
- Date: Mon, 4 Feb 2002 20:09:27 -0800
On Monday, February 4, 2002, at 02:21 PM, Erik M. Buck wrote:
On Monday, February 4, 2002, at 11:14 AM, Erik M. Buck wrote:
<a great analysis of long paths taking a long time to render in
quartz>
I did my own benchmarks and actually found that the pixel rate for
drawing individual line segments (via [NSBezierPath
strokeLineFromPoint:toPoint]) was nearly 60 times faster for 500 lines
approximately 200 pixels long, with randomly chosen endpoints. I got
around 8.6Kpix/sec stroking the full path and around 500Kpix/sec drawing
each line segment by itself.
But, this simply isn't an apples to apples comparison. Think about the
work that must be done to correctly render a path that has 500
overlapping, anti-aliased, joined, semi-transparent line segments.
There are thousands of overlapped lines... probably tens of thousands of
intersections... it can't simply render each line in sequence... it has
to compute the WHOLE path as a single polygon, otherwise the points that
overlapped would get darker each time they crossed... not to mention
computing the joins and not having them cause funny rendering... It's
miraculous that they end up rendering the correct picture when it
eventually finishes... (some 11 seconds later for 500 lines)
It's a hard problem... I don't think you're going to find that the Apple
engineers are just slackers and chose a bone-headed O(N*N) algorithm
because they aren't smart enough to do better.
Note that if you turn off Anti-aliasing, the full path case improves
tremendously... since the algorithm is much simpler.
Here's the data I got (in thousands of pixels per second)
AA no-AA
full path 9 295
segments 500 820
i just did a comparison to x11 running on the same hardware... x11 fills
6.7 million pixels a second drawing 100 pixel long lines, no
anti-aliasing, no alpha... so around 8X faster than quartz in the same
mode. (note 500 pixel lines in x11 are filled at an astounding 32
million pixels per second!)
Just getting carried away now, I added a simple rect fill test... x11
gets around 40mm pix/sec and quartz (via NSRectFill) gets 63mm
pix/sec... so the performance deficit isn't global.
It would be good if someone had the time to port x11perf in its entirety
to quartz.
Here's a view you can test this with... really shows the difference
between what the two methods have to do.
throw this code in as a custom view class... hook up in IB three
checkboxes, one to set/unset antialiasing (target setAA:), one to switch
between line segments and the whole path (target setEach:), and another
to toggle between 0.1 and 1.0 transparency... you can throw a button in
that targets go: to cause the view to redisplay/retime. add a textField
and hook it up to results outlet to get the timing results displayed.
I put the project on my iDisk
http://homepage.mac.com/tom_waters/.cv/tom_waters/Public/QuartzBench.tar.gz-binhex.
hqx
#import <AppKit/AppKit.h>
@interface BenchView : NSView {
long pix;
int n;
NSPoint *points;
NSBezierPath *path;
BOOL aa;
BOOL each;
float alpha;
IBOutlet id results;
}
@end
@implementation BenchView
- (void)dealloc
{
NSZoneFree(NSDefaultMallocZone(), points);
[path release];
}
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
int i;
n = 501;
pix = 0;
path = [[NSBezierPath bezierPath] retain];
[path setLineJoinStyle:NSBevelLineJoinStyle];
points = NSZoneMalloc(NSDefaultMallocZone(), n * sizeof(NSPoint));
for (i = 0; i < n; i++) {
float x = frame.size.width * random() / (float)0x7fffffff;
float y = frame.size.height * random() / (float)0x7fffffff;
points[i] = NSMakePoint(x, y);
if (i > 0) {
NSPoint p = points[i-1];
pix += sqrt((x-p.x)*(x-p.x)+ (y-p.y)*(y-p.y));
[path lineToPoint:points[i]];
} else {
[path moveToPoint:points[0]];
}
}
each = NO;
aa = NO;
alpha = 1.0;
NSLog(@"lines are %d long on average", pix / (n-1));
[NSBezierPath setDefaultLineWidth:0.0];
}
return self;
}
- (IBAction)go:(id)sender
{
[self setNeedsDisplay:YES];
}
- (IBAction)setEach:(id)sender
{
each = [sender intValue];
[self setNeedsDisplay:YES];
}
- (IBAction)setAA:(id)sender
{
aa = [sender intValue];
[self setNeedsDisplay:YES];
}
- (IBAction)setAlpha:(id)sender
{
alpha = [sender intValue] ? 0.1 : 1.0;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)rect {
NSDate *start;
NSTimeInterval drawTime;
int i;
[[NSGraphicsContext currentContext] setShouldAntialias:aa];
[[NSColor whiteColor] set];
NSRectFill(rect);
[[[NSColor blackColor]colorWithAlphaComponent:alpha] set];
start = [NSDate date];
if (each) {
for (i = 0; i < n-1; i++) {
[NSBezierPath strokeLineFromPoint:points[i] toPoint:points[i+1]];
}
} else {
[path stroke];
}
drawTime = -[start timeIntervalSinceNow];
[results setStringValue:[NSString stringWithFormat:@"%g (%g
kpix/sec)", drawTime, pix/drawTime/1000]];
}
@end