[Zodb-checkins] CVS: ZODB3/ZEO - simul.py:1.3
Guido van Rossum
guido@python.org
Fri, 6 Sep 2002 11:31:01 -0400
Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv1305
Modified Files:
simul.py
Log Message:
Refactored to make the simulation a class. Tweaked output formatting,
report overall statistics for all columns.
=== ZODB3/ZEO/simul.py 1.2 => 1.3 ===
--- ZODB3/ZEO/simul.py:1.2 Fri Sep 6 01:03:00 2002
+++ ZODB3/ZEO/simul.py Fri Sep 6 11:31:01 2002
@@ -51,103 +51,175 @@
print "can't open %s: %s" % (filename, msg)
return 1
- # Set up statistics
- flips = 0
- loads = 0
- hits = 0
- invals = 0
- writes = 0
- ts0 = None
- total_loads = 0
- total_hits = 0
-
- # Set up simulation data
- filelimit = cachelimit / 2
- filesize = [4, 4] # account for magic number
- fileoids = [{}, {}]
- current = 0 # index into filesize, fileoids
-
- # Print header
- print "%12s %12s %6s %6s %6s %6s %6s %6s" % (
- "__START_TIME", "___STOP_TIME", "LOADS", "HITS",
- "INVALS", "WRITES", "FLIPS", "HIT%")
+ # Create simulation object
+ sim = ZEOCacheSimulation(cachelimit)
+
+ # Print output header
+ sim.printheader()
# Read trace file, simulating cache behavior
while 1:
- # Read a reacord
+ # Read a record
r = f.read(24)
if len(r) < 24:
break
-
# Decode it
ts, code, oid, serial = struct.unpack(">ii8s8s", r)
- dlen, version, code, ignored = (code & 0x7fffff00,
+ dlen, version, code, current = (code & 0x7fffff00,
code & 0x80,
code & 0x7e,
code & 0x01)
- if ts0 is None:
- ts0 = ts
+ # And pass it to the simulation
+ sim.event(ts, dlen, version, code, current, oid, serial)
+
+ # Finish simulation
+ sim.finish()
+
+ # Exit code from main()
+ return 0
+
+class Simulation:
+
+ """Abstract base class to define simulation interface.
+
+ These are the only methods that the driver program calls.
+
+ The constructor signature is not part of the interface.
+
+ """
+
+ def event(self, ts, dlen, version, code, current, oid, serial):
+ pass
+
+ def printheader(self):
+ pass
+
+ def finish(self):
+ pass
+
+class ZEOCacheSimulation(Simulation):
+
+ """Simulate the current (ZEO 1.0 and 2.0) ZEO cache behavior."""
+
+ def __init__(self, cachelimit):
+ # Store simulation parameters
+ self.filelimit = cachelimit / 2
+ # Initialize global statistics
+ self.epoch = None
+ self.total_flips = 0
+ self.total_loads = 0
+ self.total_hits = 0
+ self.total_invals = 0
+ self.total_writes = 0
+ # Reset per-run statistics and simulation data
+ self.restart()
+
+ def restart(self):
+ # Set up statistics
+ self.flips = 0
+ self.loads = 0
+ self.hits = 0
+ self.invals = 0
+ self.writes = 0
+ self.ts0 = None
+ # Set up simulation data
+ self.filesize = [4, 4] # account for magic number
+ self.fileoids = [{}, {}]
+ self.current = 0 # index into filesize, fileoids
+
+ def event(self, ts, dlen, _version, code, _current, oid, _serial):
+ # Record first and last timestamp seen
+ if self.ts0 is None:
+ self.ts0 = ts
+ if self.epoch is None:
+ self.epoch = ts
+ self.ts1 = ts
# Simulate cache behavior. Use load hits, updates and stores
# only (each load miss is followed immediately by a store
# unless the object in fact did not exist). Updates always write.
if dlen and code & 0x70 in (0x20, 0x30, 0x50):
if code == 0x3A:
- writes += 1
+ self.writes += 1
+ self.total_writes += 1
else:
- loads += 1
- total_loads += 1
- if code != 0x3A and (fileoids[current].get(oid) or
- fileoids[1-current].get(oid)):
- hits += 1
- total_hits += 1
+ self.loads += 1
+ self.total_loads += 1
+ if code != 0x3A and (self.fileoids[self.current].get(oid) or
+ self.fileoids[1 - self.current].get(oid)):
+ self.hits += 1
+ self.total_hits += 1
else:
# Simulate a miss+store. Fudge because dlen is
# rounded up to multiples of 256. (31 is header
- # overhead per cache record; 8 is min data size.)
- dlen = max(31 + 8, dlen + 31 - 128)
- if filesize[current] + dlen > filelimit:
+ # overhead per cache record; 127 is to compensate for
+ # rounding up to multiples of 256.)
+ dlen = dlen + 31 - 127
+ if self.filesize[self.current] + dlen > self.filelimit:
# Cache flip
- flips += 1
- current = 1 - current
- filesize[current] = 4
- fileoids[current] = {}
- filesize[current] += dlen
- fileoids[current][oid] = 1
+ self.flips += 1
+ self.total_flips += 1
+ self.current = 1 - self.current
+ self.filesize[self.current] = 4
+ self.fileoids[self.current] = {}
+ self.filesize[self.current] += dlen
+ self.fileoids[self.current][oid] = 1
elif code & 0x70 == 0x10:
# Invalidate
- if fileoids[current].get(oid):
- invals += 1
- del fileoids[current][oid]
- elif fileoids[1-current].get(oid):
- invals += 1
- del fileoids[1-current][oid]
+ if self.fileoids[self.current].get(oid):
+ self.invals += 1
+ self.total_invals += 1
+ del self.fileoids[self.current][oid]
+ elif self.fileoids[1 - self.current].get(oid):
+ self.invals += 1
+ self.total_invals += 1
+ del self.fileoids[1 - self.current][oid]
elif code == 0x00:
# Restart
- if loads:
- report(ts0, ts, loads, hits, invals, writes, flips)
- loads = 0
- hits = 0
- flips = 0
- invals = 0
- writes = 0
- ts0 = None
- filesize = [4, 4] # account for magic number
- fileoids = [{}, {}]
- current = 0 # index into filesize, fileoids
-
- if loads:
- report(ts0, ts, loads, hits, invals, writes, flips)
-
- if total_loads:
- print "Overall: %d loads, %d hits, hit rate %.1f%%" % (
- total_loads, total_hits, 100.0 * total_hits / total_loads)
-
-def report(ts0, ts, loads, hits, invals, writes, flips):
- hr = 100.0 * hits / max(loads, 1)
- print "%s %s %6d %6d %6d %6d %6d %6.1f%%" % (
- time.ctime(ts0)[4:-8], time.ctime(ts)[4:-8],
- loads, hits, invals, writes, flips, hr)
+ self.report()
+ self.restart()
+
+ format = "%12s %9s %8s %8s %6s %6s %5s %6s"
+
+ def printheader(self):
+ print self.format % (
+ "START TIME", "DURATION", "LOADS", "HITS",
+ "INVALS", "WRITES", "FLIPS", "HITRATE")
+
+ def report(self):
+ if self.loads:
+ print self.format % (
+ time.ctime(self.ts0)[4:-8],
+ duration(self.ts1 - self.ts0),
+ self.loads, self.hits, self.invals, self.writes, self.flips,
+ hitrate(self.loads, self.hits))
+
+ def finish(self):
+ if self.loads:
+ self.report()
+ if self.total_loads:
+ print (self.format + " OVERALL") % (
+ time.ctime(self.epoch)[4:-8],
+ duration(self.ts1 - self.epoch),
+ self.total_loads,
+ self.total_hits,
+ self.total_invals,
+ self.total_writes,
+ self.total_flips,
+ hitrate(self.total_loads, self.total_hits))
+
+def hitrate(loads, hits):
+ return "%5.1f%%" % (100.0 * hits / max(1, loads))
+
+def duration(secs):
+
+ mm, ss = divmod(secs, 60)
+ hh, mm = divmod(mm, 60)
+ if hh:
+ return "%d:%02d:%02d" % (hh, mm, ss)
+ if mm:
+ return "%d:%02d" % (mm, ss)
+ return "%d" % ss
if __name__ == "__main__":
sys.exit(main())