Hello All,
In November of 1999 I did some benchmark tests to gauge the performance
difference between Tkinter and wxPython when large numbers of lines are
plotted. At that time wxPython outperformed Tkinter by a large margin as
can be seen in the results that are repeated below. As a full year has
passed and there are new versions of Python, Tkinter and wxPython, I felt
that it was time to rerun the tests to see if there were any significant
changes for better or worst.
While Tkinter has improved significantly, it is still far behind wxPython in
this aspect by a margin of 20 to 30 times.
The same computer was used to generate the new set of results, so the
results are directly comparable. As described below, a repeating sine wave
was created and put into a horizontal scrollable window. These are the
results
for the line plotting time only. Time in Seconds. (W95, Python 2.0,
wxPython 2.2.2)
Points Tkinter Tkinter Tkinter wxPython wxPython
(standard) (mod 1) (mod 2) (direct draw) (bitmap)
1000 0.163 0.162 0.157 0.0078 0.028
2000 0.324 -- -- 0.0130
0.041
5000 0.817 -- -- 0.0390
0.092
10000 1.62 1.63 1.64 0.0590 0.157
See the notes below for more information on the configuration for each run.
Conclusions:
1) The biggest change is in the performance of Tkinter (standard).
Previously, the time to plot lines using the standard Tkinter was quadratic
with the
number of points (ugh!). Now it is linear and the results are very similar
to both of the modified Tkinter runs. An improvement was made to speed up
the_flatten method where most of the time was spent previously.
2) Tkinter mod 1 and 2 give almost the same results. There is a slight
improvement over the last years results by 15 to 20%. Some other changes
must have been made to the code to get these efficiencies.
3) wxPython results have changed very little. Maybe they have gotten 5 to
10% slower, but it is difficult to say without doing a more though study.
Direct drawing onto to screen is still roughly 3 times faster than using a
bitmap.
4) wxPython still beats Tkinter by a wide margin. Although the number are
not as huge as they were a year ago, wx still goes 20 - 30 times faster.
If you need raw speed to get large numbers of lines out to the screen,
wxPython is the way to go.
It would be interesting to have this repeated on a Unix box to get
comparable results.
Regards,
Gordon Williams
Dec. 2000
···
########################################################
November 1999 (Windows 95, Python 1.5.2, wxPython ?)
I have done some benchmark testing to see the difference in speed between
plotting multi point lines using Tkinter and wxPython. I have looked at 3
different methods for doing it in Tk and 2 different methods in wx. The
differences are astounding for the 1000 to 10000 point lines that I
examined. Generally, wx was found to be in the order of 200 to 1800 times
faster than the standard Tkinter. Tk can be sped up by ten to 50 times by
changing Tkinter so it bypasses the _flatten method or by making the Tk
calls directly.
A repeating sine wave was created and put into a horizontal scrollable
window of about 500 points wide and 300 points high. These are the results
for the line plotting time only.Time in Seconds on a P166 with 96MB RAM.
Points Tkinter Tkinter Tkinter wxPython wxPython
(standard) (mod 1) (mod 2) (direct draw) (bitmap)
1000 1.190 0.192 .202 0.007 0.026
2000 4.406 0.385 -- 0.014 0.042
5000 25.170 0.967 -- 0.027 0.080
10000 100.260 1.941 2.064 0.054 0.148
Notes:
1) Tk standard is the standard usage with no modifications.
2) Tk mod 1 has Tkinter modified so a flat tuple of points is passed to
create_line and _flatten is by-passed.
3) Tk mod 2 used the apply method e.g.
apply(self.can.tk.call,(self.can._w,'create','line') + points +
('-width',1,'-fill',colour,'-tags',"tag"))
4) wx direct draw draws to the window directly.
5) wx bitmap draws to a bitmap and then blit
Conclusions:
1) The time to plot lines using the standard Tkinter is quadratic with the
number of points (ugh!). This is due to the time spent in _flatten.
_flatten is very versatile but agonizingly slow for large numbers of lines.
2) Tkinter mod 1 and 2 give the same results. Times are linear with the
number of points plotted as both cases dont use the _flatten method. This
means that a flat tuple of points has to be used (a small constraint). The
speed increase in doing this is from 10 to 50 times over the standard.
3) wxPython beats Tk by a very! large margin even when Tk is called
directly. Direct drawing onto to screen is roughly 3 times faster than
using a bitmap.
Thanks to:
Greg Landrum for providing some information on direct calls to tk.
Ionel Simionescu for providing the wxPython code that I was able to modify.
############################################################
I know I am going to get questions on where is the code, so here it is. I
have not supplied my version of Tkinter (Tkinter_gw.py) because it is large.
############################################################
#Ionel2_test_spd.py (for draw to bitmap)
__author__ = "Ionel Simionescu" # messed up by Gordon Williams
#---------------------------------------------------------------------------
from wxPython.wx import *
import time
from math import sin
#---------------------------------------------------------------------------
t0 = time.clock()
N = 1000 # the number of line points
points = [] # here we store the points
for k in range(N):
points.append( (k, 50+ 50*sin(k/10.)) )
#print points
#print points
t1 = time.clock()
print 'Data is ready! Total data preparation time: %f ' % (t1 - t0)
#---------------------------------------------------------------------------
bmpW = N #changed
bmpH = 300
#---------------------------------------------------------------------------
class myCanvas(wxScrolledWindow):
def __init__(self, parent, ID, position, size):
wxScrolledWindow.__init__(self, parent, ID, position, size,
wxSUNKEN_BORDER)
self.SetBackgroundColour(wxNamedColor("WHITE"))
self.maxWidth = 500
self.maxHeight = 300
self.SetScrollbars(20, 0, N/20, 0) #self.maxWidth/20,
self.maxHeight/20)
self.bmp = wxEmptyBitmap(bmpW, bmpH, -1)
#self.bmp.Create(bmpW, bmpH, -1)
#
self.bmp_w = self.bmp.GetWidth()
self.bmp_h = self.bmp.GetHeight()
# flag used to redraw only when necessary
self.new_data = 1
EVT_PAINT(self, self.OnPaint)
def OnPaint(self, event):
dc = wxPaintDC(self)
self.PrepareDC(dc)
self.DoDrawing(dc)
def DoDrawing(self, dc):
t0 = time.clock()
pen1 = wxPen(wxNamedColour('RED'))
tmp_dc = wxMemoryDC()
tmp_dc.SelectObject(self.bmp)
# if new data, update the bitmap drawing
if self.new_data:
tmp_dc.BeginDrawing()
tmp_dc.Clear()
tmp_dc.SetPen(pen1)
# draw the lines
tmp_dc.DrawLines( points ) #changed
tmp_dc.EndDrawing()
# reset the new_data flag
self.new_data = 0
# copy the bitmap to the canvas
dc.Blit( 0, 0, self.bmp_w, self.bmp_h, tmp_dc, 0, 0 )
t1 = time.clock()
print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)
#---------------------------------------------------------------------------
class myFrame(wxFrame):
def __init__(self):
wxFrame.__init__(
self, NULL, -1, "wx_plot_lines",
wxDefaultPosition, size= (500,300)#wxSize(500, 300)
)
myCanvas(self, -1, wxPoint(0,0), self.GetClientSize())
#---------------------------------------------------------------------------
class myApp(wxApp):
def OnInit(self):
myFrame().Show(TRUE)
return TRUE
#---------------------------------------------------------------------------
if __name__ == '__main__':
app = myApp(0)
app.MainLoop()
#######################################################
#Ionel1_test_spd.py (for direct draw method)
__author__ = "Ionel Simionescu" # messed up by Gordon Williams
#---------------------------------------------------------------------------
from wxPython.wx import *
from math import sin
import time
#---------------------------------------------------------------------------
t0 = time.clock()
N = 10000 # the number of line points
points = [] # here we store the points
for k in range(N):
points.append( (k, 50+ 50*sin(k/10.)) )
#print points
t1 = time.clock()
print 'Data is ready! Total data preparation time: %f ' % (t1 - t0)
#---------------------------------------------------------------------------
class myCanvas(wxScrolledWindow):
def __init__(self, parent, ID, position, size):
wxScrolledWindow.__init__(self, parent, ID, position, size,
wxSUNKEN_BORDER)
self.SetBackgroundColour(wxNamedColor("WHITE"))
self.maxWidth = 500
self.maxHeight = 300
self.SetScrollbars(20, 0, N/20, 0) #self.maxWidth/20,
self.maxHeight/20)
EVT_PAINT(self, self.OnPaint)
def OnPaint(self, event):
dc = wxPaintDC(self)
self.PrepareDC(dc)
self.DoDrawing1(dc)
def DoDrawing1(self, dc):
t0 = time.clock()
dc.BeginDrawing()
pen1 = wxPen(wxNamedColour('RED'))
dc.SetPen(pen1)
#
# draw line_points
dc.DrawLines(points, 0)
#
dc.EndDrawing()
t1 = time.clock()
print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)
class myFrame(wxFrame):
def __init__(self):
wxFrame.__init__(
self, NULL, -1, "wx_plot_lines",
wxDefaultPosition,wxSize(500,300)
)
myCanvas(self, -1, wxPoint(0,0), self.GetClientSize())
#---------------------------------------------------------------------------
class myApp(wxApp):
def OnInit(self):
myFrame().Show(TRUE)
return TRUE
#---------------------------------------------------------------------------
def test():
app = myApp(0)
app.MainLoop()
if __name__ == '__main__':
test()
##########################################################
# File for benchmarking multi-point lines
# in wxPython and Tkinter.
#
# This file is for Tkinter
# Change the commented sections as required.
#
# Author: Gordon Williams 99/11/15
#
import time
from math import sin
#from Tkinter_gw import *
from Tkinter import *
class LayoutFrames:
def __init__(self,master):
# ********** Frame Layouts ***************
# Main Frame Layout
self.data= Frame(master)
self.data.pack(side=TOP,fill=X)
self.scroll= Frame(self.data)
self.scroll.pack(side=BOTTOM,fill=X)
class Plot:
def __init__(self, master, master_scroll=None):
# dataframe with canvas and scollbar
self.scrollbar = Scrollbar(master_scroll, orient=HORIZONTAL)
self.can = Canvas(master,cursor="crosshair", width=500,
height=300,relief=RIDGE, bd=2,bg="lightyellow",
xscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.can.xview)
self.scrollbar.pack(side=BOTTOM, fill=X)
self.can.pack(side=LEFT)
def makelines(self,points, tag="misc",colour="black"):
t0 = time.clock()
#>>>> comment out as required (use for standard and mod1 runs)
#self.can.create_line(points,width= 1, tags=tag, fill=colour)
#>>>> comment out as required (use for mod2 runs)
apply(self.can.tk.call,(self.can._w,'create','line') + points +
('-width',1,'-fill',colour,'-tags',"tag"));
self.can.config(scrollregion=self.can.bbox(ALL))
t1 = time.clock()
print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)
class PlotResults:
def __init__(self, dataFrame, scrollFrame):
# ********** PLOTTING DATA ***************
self.analog = Plot(dataFrame,scrollFrame)
def analogPlot(self):
self.analog.makelines(linePoints, "dataA","black" )
#************ start of main **************************
def main():
root= Tk()
frame=LayoutFrames(root)
analog=PlotResults( frame.data, frame.scroll)
analog.analogPlot()
root.mainloop()
try:
root.destroy() # removes root
except:
pass
# make a bit of data
N= 1000 # number of points
points = [] # here we store the points
for k in range(N):
points.extend( [k, 50+ 50*sin(k/10.)] )
#print points
linePoints= tuple(points) # must be in tupple form for Tkinter_gw
main()
#######################################################
Enjoy!!
_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users