Attachment "2874226 - -joinstyle - performance checks.tcl" to
ticket [2874226f]
added by
fvogel
2017-08-19 16:03:39.
# This is an analysis of the performance impact of the fix for ticket
# 2874226 - polygon don't honor -joinstyle on Windows and OS X
# Linux: no bug, no fix
# Windows: fix exists: [c441e73f]
# OS X: fix exists: [abd26a6e]
#
# This performacne impact analysis deals with Windows only.
# core-8-6-branch and the bugfix branch are compiled in release mode with no special option
####################################################
####################################################
With reference [c441e73f] (aka the "bugfix"), a candidate "optimized bugfix" for Windows only (no such need for OS X) could be:
C:\Users\francois\Documents\Development\tcltk-fossil\tk>f diff
Index: win/tkWinDraw.c
==================================================================
--- win/tkWinDraw.c
+++ win/tkWinDraw.c
@@ -739,15 +739,17 @@
}
?
/*
*----------------------------------------------------------------------
*
- * MakeAndStrokePath --
+ * DrawPoly --
*
* This function draws a shape using a list of points, a stipple pattern,
- * and the specified drawing function. It does it through creation of a
- * so-called 'path' (see GDI documentation on MSDN).
+ * and the specified drawing function. Closed polylines are drawn through
+ * creation of a so-called 'path' (see GDI documentation on MSDN).
+ * Open polylines and polygons are drawn a tiny bit quicker, without
+ * resorting to a path.
*
* Results:
* None.
*
* Side effects:
@@ -754,36 +756,40 @@
* None.
*
*----------------------------------------------------------------------
*/
static void
-MakeAndStrokePath(
+DrawPoly(
HDC dc,
POINT *winPoints,
int npoints,
WinDrawFunc func) /* Name of the Windows GDI drawing function:
this is either Polyline or Polygon. */
{
- BeginPath(dc);
- func(dc, winPoints, npoints);
/*
- * In the case of closed polylines, the first and last points
- * are the same. We want miter or bevel join be rendered also
- * at this point, this needs telling the Windows GDI that the
- * path is closed.
- */
+ * In case of a closed polyline, where the first and last points are
+ * the same, we want miter or bevel join be rendered also at this point.
+ * To get this result, the Windows GDI needs to be told explicitely
+ * that the polyline is closed. This needs creation of a 'path'.
+ */
if (func == Polyline) {
if ((winPoints[0].x == winPoints[npoints-1].x) &&
(winPoints[0].y == winPoints[npoints-1].y)) {
+ BeginPath(dc);
+ func(dc, winPoints, npoints);
CloseFigure(dc);
+ EndPath(dc);
+ StrokePath(dc);
+ return;
}
- EndPath(dc);
- StrokePath(dc);
- } else {
- EndPath(dc);
- StrokeAndFillPath(dc);
}
+
+ /*
+ * In all other cases, a path is not needed (and is not used to avoid
+ * any performance loss, be it very small).
+ */
+ func(dc, winPoints, npoints);
}
?
/*
*----------------------------------------------------------------------
*
@@ -877,11 +883,11 @@
*/
SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE
: WINDING);
oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground));
- MakeAndStrokePath(dcMem, winPoints, npoints, func);
+ DrawPoly(dcMem, winPoints, npoints, func);
BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG);
/*
* If we are rendering an opaque stipple, then draw the polygon in the
* background color and copy it to the destination wherever the
@@ -889,11 +895,11 @@
*/
if (gc->fill_style == FillOpaqueStippled) {
DeleteObject(SelectObject(dcMem,
CreateSolidBrush(gc->background)));
- MakeAndStrokePath(dcMem, winPoints, npoints, func);
+ DrawPoly(dcMem, winPoints, npoints, func);
BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0,
COPYBG);
}
SelectObject(dcMem, oldPen);
@@ -905,11 +911,11 @@
oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground));
SetROP2(dc, tkpWinRopModes[gc->function]);
SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE
: WINDING);
- MakeAndStrokePath(dc, winPoints, npoints, func);
+ DrawPoly(dc, winPoints, npoints, func);
SelectObject(dc, oldPen);
}
DeleteObject(SelectObject(dc, oldBrush));
}
?
####################################################
####################################################
####################################################
# performance non-regression testing: polygon
package require Tk
pack [canvas .c]
proc gencoords {n {maxx 400} {maxy 250}} {
set res {}
for {set i 1} {$i <= $n} {incr i} {
set x [expr {round(rand() * $maxx)}]
set y [expr {round(rand() * $maxy)}]
lappend res $x $y
}
return $res
}
proc drawit {repeattest nbvertices} {
for {set i 1} {$i <= $repeattest} {incr i} {
set id [.c create polygon [gencoords $nbvertices]]
.c itemconfigure $id -fill red -outline black -width 10 -joinstyle miter
update
.c delete $id
}
}
proc timeit {repeatdraw nbvertices {nb 3}} {
set tot 0
for {set i 1} {$i <= $nb} {incr i} {
incr tot [scan [time {drawit $repeatdraw $nbvertices}] %d]
}
puts "Mean time on $nb runs: [expr {$tot / $nb}] �s"
}
timeit 500 300 3
timeit 2000 30 3
timeit 5000 50 10
# results (in �s): core-8-6-branch bugfix branch optimized bugfix perf loss perf loss perf loss
# [6dc1b349] [c441e73f] [... ? ...] 86->bugfix bugfix->opti 86->optim
# timeit 500 300 3 5203843 5034738 5069571 -3,2% 0,7% -2,6%
# timeit 2000 30 3 3726699 3433176 3432480 -7,9% 0,0% -7,9%
# timeit 5000 50 10 10278187 9929803 10008726 -3,4% 0,8% -2,6%
Therefore:
Using paths is more performant than not using them.
The optimized fix in fact does not optimize because it does not use a path for non-closed polylines.
BUT:
All these figures are in the uncertainty margin. When running the tests later again figures were different. Depends on the computer use I guess.
What's important is that the figures should be comparable since tests were run next to each other in a short timeframe, thus no performance loss.
####################################################
# performance non-regression testing: (poly)line
package require Tk
pack [canvas .c]
proc gencoords {n closeline {maxx 400} {maxy 250}} {
set res {}
for {set i 1} {$i <= $n} {incr i} {
set x [expr {round(rand() * $maxx)}]
set y [expr {round(rand() * $maxy)}]
lappend res $x $y
}
if {$closeline} {
# close the polyline
lappend res [lindex $res 0]
lappend res [lindex $res 1]
}
return $res
}
proc drawit {repeatdraw nbvertices closeline} {
for {set i 1} {$i <= $repeatdraw} {incr i} {
set id [.c create line [gencoords $nbvertices $closeline]]
.c itemconfigure $id -fill red -width 10 -joinstyle miter
update
.c delete $id
}
}
proc timeit {repeatdraw nbvertices {nb 3} {closeline true}} {
set tot 0
for {set i 1} {$i <= $nb} {incr i} {
incr tot [scan [time {drawit $repeatdraw $nbvertices $closeline}] %d]
}
puts "Mean time on $nb runs: [expr {$tot / $nb}] �s"
}
timeit 500 300 3
timeit 500 300 3 0
timeit 2000 30 3
timeit 2000 30 3 0
timeit 5000 50 10
timeit 5000 50 10 0
# results (in �s): core-8-6-branch bugfix branch optimized bugfix perf loss perf loss perf loss
# [6dc1b349] [c441e73f] [... ? ...] 86->bugfix bugfix->opti 86->optim
# timeit 500 300 3 4516433 4486570 4495222 -0,7% 0,2% -0,5%
# timeit 500 300 3 0 4495286 4436930 4424865 -1,3% -0,3% -1,6%
# timeit 2000 30 3 3237309 3194199 3180000 -1,3% -0,4% -1,8%
# timeit 2000 30 3 0 3163584 3161066 3133370 -0,1% -0,9% -1,0%
# timeit 5000 50 10 9113923 9029612 9047344 -0,9% 0,2% -0,7%
# timeit 5000 50 10 0 9020690 9005947 8934129 -0,2% -0,8% -1,0%
Therefore:
Using paths is generally more performant than not using them.
Slight additional performance gain with the optimized fix when the polyline is not closed.
For closed polylines the optimized fix is in fact a very small loss of performance.
BUT:
All these figures are in the uncertainty margin. When running the tests later again figures were different. Depends on the computer use I guess.
What's important is that the figures should be comparable since tests were run next to each other in a short timeframe, thus no performance loss.
####################################################
GENERAL CONCLUSION:
All in all the optimized fix is worse than non-optimized fix for polygons and non-closed polylines.
Paths are more performant than not using them.
The optimized fix does provide a small performance increase for non-closed polylines, but this is more than balanced by the performance loss with polygons and closed polylines, especially since the measured performance increase is marginal compared to the uncertainty of the measurement.
--> 1. The fix does not create any loss of performance. It rather improves performance slightly.
2. The optimized fix is rejected, and the bugfix [c441e73f] is kept as is.