[Zodb-checkins] CVS: ZODB3/ZEO - simul.py:1.1
Guido van Rossum
guido@python.org
Fri, 6 Sep 2002 00:00:27 -0400
Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv12624
Added Files:
simul.py
Log Message:
Cache simulation. Reads a trace file as dumped by ClientCache.py with
ZEO_CACHE_TRACE set (see stats.py for file format description).
=== Added File ZODB3/ZEO/simul.py ===
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Cache simulation.
Usage: simul.py -s size tracefile
-s size: cache size in MB (default 20 MB)
"""
import sys
import time
import getopt
import struct
def usage(msg):
print >>sys.stderr, msg
print >>sys.stderr, __doc__
def main():
# Parse options
cachelimit = 20*1000*1000
try:
opts, args = getopt.getopt(sys.argv[1:], "s:")
except getopt.error, msg:
usage(msg)
return 2
for o, a in opts:
if o == '-s':
cachelimit = int(float(a) * 1e6)
if len(args) != 1:
usage("exactly one file argument required")
return 2
filename = args[0]
# Open file
try:
f = open(filename, "rb")
except IOError, msg:
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%")
# Read trace file, simulating cache behavior
while 1:
# Read a reacord
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,
code & 0x80,
code & 0x7e,
code & 0x01)
if ts0 is None:
ts0 = 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
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
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:
# Cache flip
flips += 1
current = 1 - current
filesize[current] = 4
fileoids[current] = {}
filesize[current] += dlen
fileoids[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]
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)
if __name__ == "__main__":
sys.exit(main())