root/trunk/benchmark.py

Revision 162, 8.5 KB (checked in by mg, 6 years ago)

Move all modules into a package (pyspacewar), which resides inside a src/
subdirectory (to avoid clashes with the pyspacewar script).

Now python setup.py bdist seems to work (there are extra files in the
tarball, but I think that's because I was afraid to run setup.py clean
without checking everything in).

The setup script now requires python 2.4 (for package_data).

  • Property svn:executable set to *
  • Property svn:keywords set to Id
Line 
1#!/usr/bin/env python
2"""
3PySpaceWar benchmarks for optimisation work
4
5$Id$
6"""
7
8import os
9import sys
10import time
11import random
12import optparse
13
14import pygame
15from pygame.locals import *
16
17
18def setup_path():
19    """Set up python path if running from a source tree."""
20    pkgdir = os.path.join(os.path.dirname(__file__), 'src')
21    print pkgdir
22    if os.path.isdir(pkgdir):
23        sys.path.insert(0, pkgdir)
24
25setup_path()
26
27
28from pyspacewar.game import Game
29from pyspacewar.world import Ship
30from pyspacewar.ai import AIController
31from pyspacewar.ui import GameUI
32
33
34def get_cpu_speed():
35    try:
36        rows = [row for row in file('/proc/cpuinfo')
37                if row.startswith('cpu MHz')]
38    except IOError:
39        return 0
40    try:
41        return float(rows[0].split(':')[1])
42    except IndexError:
43        return 0
44
45
46class DummyAIController(object):
47
48    def __init__(self, ship):
49        self.ship = ship
50
51    def control(self):
52        if self.ship.appearance % 2:
53            self.ship.turn_left()
54        else:
55            self.ship.turn_right()
56        if len(self.ship.world.objects) < 90:
57            self.ship.launch()
58
59class DummyTimeSource(object):
60
61    delta = 0
62
63    def now(self):
64        return 0
65
66    def wait(self, time):
67        return True
68
69
70class Stats(object):
71    time = 0
72    ticks = 0
73    max_objects = 0
74    min_objects = sys.maxint
75    total_objects = 0
76    best_time = sys.maxint
77    worst_time = 0
78
79    @property
80    def avg_objects(self):
81        if self.ticks == 0:
82            return 0
83        return self.total_objects / float(self.ticks)
84
85    @property
86    def ticks_per_second(self):
87        if self.time == 0:
88            return 0
89        return self.ticks / float(self.time)
90
91    @property
92    def ms_per_tick(self):
93        if self.ticks == 0:
94            return 0
95        return self.time * 1000.0 / self.ticks
96
97
98class Benchmark(object):
99
100    def __init__(self, seed=None, how_long=100,
101                 ai_controller=DummyAIController, warmup_ticks=0,
102                 profile=False, debug=False):
103        self.seed = seed
104        self.how_long = how_long
105        self.ai_controller = ai_controller
106        self.warmup_ticks = warmup_ticks
107        self.profile = profile
108        self.debug = debug
109
110    def run(self):
111        self.stats = Stats()
112        self.init()
113        self.stats.cpu_speed_before_warmup = get_cpu_speed()
114        self.warmup()
115        self.stats.cpu_speed_after_warmup = get_cpu_speed()
116        if self.profile:
117            from profile import Profile
118            profiler = Profile()
119            profiler.runcall(self.benchmark)
120        else:
121            self.benchmark()
122        self.stats.cpu_speed_after_benchmark = get_cpu_speed()
123        if self.profile:
124            import pstats
125            self.stats.profile_stats = pstats.Stats(profiler)
126        return self.stats
127
128    def warmup(self):
129        tick = 0
130        while tick < self.warmup_ticks:
131            self.cycle()
132            tick += 1
133
134    def benchmark(self):
135        game = self.game
136        stats = self.stats
137        start = now = time.time()
138        while stats.ticks < self.how_long:
139            prev = now
140            self.cycle()
141            now = time.time()
142            stats.ticks += 1
143            stats.max_objects = max(stats.max_objects, len(game.world.objects))
144            stats.min_objects = min(stats.min_objects, len(game.world.objects))
145            stats.total_objects += len(game.world.objects)
146            stats.best_time = min(stats.best_time, now - prev)
147            stats.worst_time = max(stats.worst_time, now - prev)
148            stats.time = now - start
149
150
151class LogicBenchmark(Benchmark):
152
153    def init(self):
154        game = Game.new(ships=2, rng=random.Random(self.seed))
155        game.time_source = DummyTimeSource()
156        ships = [obj for obj in game.world.objects if isinstance(obj, Ship)]
157        game.controllers += map(self.ai_controller, ships)
158        game.wait_for_tick() # first one does nothing serious
159        self.game = game
160        self.cycle = game.wait_for_tick
161
162
163class GameBenchmark(Benchmark):
164
165    def init(self):
166        ui = GameUI()
167        ui.rng = random.Random(self.seed)
168        ui.show_debug_info = self.debug
169        ui.init()
170        for player_id, is_ai in enumerate(ui.ai_controlled):
171            if is_ai:
172                ui.toggle_ai(player_id)
173        game = ui.game
174        game.time_source = DummyTimeSource()
175        game.controllers += map(self.ai_controller, ui.ships)
176        game.wait_for_tick() # first one does nothing serious
177        self.ui = ui
178        self.game = game
179
180    def cycle(self):
181        self.ui.wait_for_tick()
182        self.ui.draw()
183        event = pygame.event.poll()
184        if (event.type == QUIT or
185            (event.type == KEYDOWN and event.key in (K_ESCAPE, K_q))):
186            self.how_long = 0
187            self.warmup_ticks = 0
188
189
190def main():
191    parser = optparse.OptionParser()
192    parser.add_option('-s', '--seed', default=0,
193                      help='specify random seed [default: %default]',
194                      action='store', dest='seed', type='int')
195    parser.add_option('-t', '--ticks', default=100,
196                      help='specify number of game ticks [default: %default]',
197                      action='store', dest='ticks', type='int')
198    parser.add_option('-w', '--warmup', default=100,
199                      help='warm up for a number of ticks [default: %default]',
200                      action='store', dest='warmup', type='int')
201    parser.add_option('-a', '--ai', default=DummyAIController,
202                      help='use real AI logic [default: dumb logic]',
203                      action='store_const', const=AIController,
204                      dest='ai_controller')
205    parser.add_option('-g', '--gui', default=LogicBenchmark,
206                      help='benchmark drawing and logic [default: just logic]',
207                      action='store_const', const=GameBenchmark,
208                      dest='benchmark')
209    parser.add_option('-d', '--debug', default=False,
210                      help='show debug info during benchmark [default: %default]',
211                      action='store_true', dest='debug')
212    parser.add_option('-p', '--profile', default=False,
213                      help='enable profiling [default: %default]',
214                      action='store_true', dest='profile')
215    parser.add_option('--psyco', default=False,
216                      help='use Psyco [default: %default]',
217                      action='store_true', dest='psyco')
218    opts, args = parser.parse_args()
219    print "=== Parameters ==="
220    print
221    if opts.psyco:
222        try:
223            import psyco
224            psyco.full()
225        except:
226            print 'psyco not available'
227        else:
228            print 'using psyco'
229    print 'random seed: %r' % opts.seed
230    print 'warmup: %d' % opts.warmup
231    print 'ticks: %d' % opts.ticks
232    print 'ai: %s' % opts.ai_controller.__name__
233    print 'benchmark: %s' % opts.benchmark.__name__
234    print 'debug: %s' % opts.debug
235    benchmark = opts.benchmark(opts.seed, opts.ticks, opts.ai_controller,
236                               opts.warmup, opts.profile, opts.debug)
237    start_time = time.time()
238    stats = benchmark.run()
239    total_time = time.time() - start_time
240    print
241    print "=== CPU ==="
242    print
243    print 'CPU speed before warmup: %.0f MHz' % stats.cpu_speed_before_warmup
244    print 'CPU speed after warmup: %.0f MHz' % stats.cpu_speed_after_warmup
245    print 'CPU speed after benchmark: %.0f MHz' % stats.cpu_speed_after_benchmark
246    print
247    print "=== Results ==="
248    print
249    print 'total time: %.3f seconds' % total_time
250    print 'ticks: %d' % stats.ticks
251    print 'objects: min=%d avg=%.1f max=%d' % (
252                stats.min_objects,
253                stats.avg_objects,
254                stats.max_objects)
255    print 'ticks per second: avg=%.3f' % stats.ticks_per_second
256    print 'ms per tick: min=%.3f avg=%.3f max=%.3f' % (
257                stats.best_time * 1000.0,
258                stats.ms_per_tick,
259                stats.worst_time * 1000.0)
260
261    if opts.profile:
262        stats = stats.profile_stats
263        stats.strip_dirs()
264        print
265        print "== Stats by internal time ==="
266        print
267        stats.sort_stats('time', 'calls')
268        stats.print_stats(40)
269        print
270        print "== Stats by number of calls, with callers ==="
271        print
272        stats.sort_stats('calls', 'time')
273        stats.print_callers(20)
274        print
275        print "== Stats by cumulative time, with calees ==="
276        print
277        stats.sort_stats('cumulative', 'calls')
278        stats.print_callees(20)
279
280if __name__ == '__main__':
281    main()
Note: See TracBrowser for help on using the browser.