bos@222: #!/usr/bin/python bos@222: # bos@222: # Silly benchmarking program, to give a vague idea of how fast a few bos@222: # tools are on a handful of common operations. bos@222: # bos@222: # Use a fairly big and real source tarball to test with: Firefox bos@222: # 2.0.0.3 (37622 files, 5374 directories, 343MB unpacked onto bos@222: # 4KB-blocksize ext3). bos@222: bos@222: import csv bos@222: import os bos@222: import shutil bos@222: import sys bos@222: import tempfile bos@222: import time bos@222: import urllib2 bos@222: bos@222: url = 'ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/2.0.0.3/source/firefox-2.0.0.3-source.tar.bz2' bos@222: bos@222: class CommandFailure(Exception): bos@222: pass bos@222: bos@222: class rcs(object): bos@222: def __init__(self): bos@222: self.logfp = open(self.__class__.__name__ + '.csv', 'w') bos@222: self.csv = csv.writer(self.logfp) bos@222: bos@222: def download(self): bos@222: name = url[url.rfind('/')+1:] bos@222: path = os.path.join(os.environ['HOME'], name) bos@222: if not os.path.isfile(path): bos@222: ofp = open(path + '.part', 'wb') bos@222: try: bos@222: ifp = urllib2.urlopen(url) bos@222: nbytes = ifp.info()['content-length'] bos@222: sys.stdout.write('%s: %s bytes ' % (name, nbytes)) bos@222: sys.stdout.flush() bos@222: while True: bos@222: data = ifp.read(131072) bos@222: if not data: break bos@222: sys.stdout.write('.') bos@222: sys.stdout.flush() bos@222: ofp.write(data) bos@222: del ofp bos@222: os.rename(path + '.part', path) bos@222: except: bos@222: if os.path.exists(path + '.part'): bos@222: os.unlink(path + '.part') bos@222: if os.path.exists(path): bos@222: os.unlink(path) bos@222: raise bos@222: return path bos@222: bos@222: def run(self, args, mustsucceed=True): bos@222: ret = os.spawnvp(os.P_WAIT, args[0], args) bos@222: if ret < 0: bos@222: msg = 'killed by signal %d' % (-ret) bos@222: if ret > 0: bos@222: msg = 'exited with status %d' % (ret) bos@222: if ret: bos@222: if mustsucceed: bos@222: raise CommandFailure('%s: %s' % (msg, ' '.join(args))) bos@222: print >> sys.stderr, 'WARNING: %s: %s' % (msg, ' '.join(args)) bos@222: bos@222: def time(self, *args, **kwargs): bos@222: start = time.time() bos@222: self.run(*args, **kwargs) bos@222: end = time.time() bos@222: return end - start bos@222: bos@222: def logtime(self, name, elapsed, rest=[]): bos@222: self.log('time:' + name, '%.3f' % elapsed, rest) bos@222: bos@222: def log(self, name, value, rest=[]): bos@222: item = (name, value, repr(rest)) bos@222: print ' '.join(item) bos@222: self.csv.writerow(item) bos@222: self.logfp.flush() bos@222: bos@222: def unpack(self): bos@222: tarball = self.download() bos@222: t = self.time(['tar', '-C', self.wdir, '-jxf', tarball]) bos@222: self.logtime('internal:untar', t) bos@222: for name in os.listdir(os.path.join(self.wdir, 'mozilla')): bos@222: os.rename(os.path.join(self.wdir, 'mozilla', name), bos@222: os.path.join(self.wdir, name)) bos@222: bos@222: def cleanup(self): bos@222: pass bos@222: bos@222: def add(self, paths): bos@222: pass bos@222: bos@222: def commit(self, msg, paths): bos@222: pass bos@222: bos@222: def status(self, path): bos@222: pass bos@222: bos@222: def remove(self, path): bos@222: pass bos@222: bos@222: bos@222: class subversion(rcs): bos@222: def __init__(self, root): bos@222: rcs.__init__(self) bos@222: self.repo = os.path.join(root, 'repo') bos@222: self.wdir = os.path.join(root, 'wc') bos@222: create = self.time(['svnadmin', 'create', '--fs-type=fsfs', self.repo]) bos@222: self.logtime('svn:create', create) bos@222: co = self.time(['svn', 'co', 'file://' + self.repo, self.wdir]) bos@222: self.logtime('svn:co', co) bos@222: self.logtime('init', create + co) bos@222: os.chdir(self.wdir) bos@222: bos@222: def dropmeta(self, names): bos@222: return [n for n in names if os.path.basename(n) != '.svn'] bos@222: bos@222: def add(self, paths): bos@222: t = self.time(['svn', 'add', '-q'] + paths) bos@222: self.logtime('add %r' % paths, t) bos@222: bos@222: def commit(self, msg, paths=[]): bos@222: if paths: bos@222: t = self.time(['svn', 'ci', '-q', '-m', msg] + paths) bos@222: else: bos@222: t = self.time(['svn', 'ci', '-q', '-m', msg]) bos@222: self.logtime('commit %r' % paths, t) bos@222: bos@222: bos@222: class mercurial(rcs): bos@222: def __init__(self, root): bos@222: rcs.__init__(self) bos@222: self.repo = os.path.join(root, 'repo') bos@222: self.wdir = self.repo bos@222: init = self.time(['hg', 'init', self.repo]) bos@222: self.logtime('init', init) bos@222: os.chdir(self.wdir) bos@222: bos@222: def dropmeta(self, names): bos@222: return [n for n in names if os.path.basename(n) != '.hg'] bos@222: bos@222: def add(self, paths): bos@222: t = self.time(['hg', 'add', '-q'] + paths) bos@222: self.logtime('add %r' % paths, t) bos@222: bos@222: def commit(self, msg, paths=[]): bos@222: if paths: bos@222: t = self.time(['hg', 'ci', '-q', '-m', msg] + paths) bos@222: else: bos@222: t = self.time(['hg', 'ci', '-q', '-m', msg]) bos@222: self.logtime('commit %r' % paths, t) bos@222: bos@222: def benchmark(cls): bos@222: oldcwd = os.getcwd() bos@222: root = tempfile.mkdtemp(prefix='sillybench.') bos@222: try: bos@222: print 'root', root bos@222: inst = cls(root) bos@222: inst.unpack() bos@222: names = inst.dropmeta(os.listdir('.')) bos@222: dirs = [n for n in names if os.path.isdir(n)] bos@222: nondirs = [n for n in names if not os.path.isdir(n)] bos@222: dirs.sort(key=hash) bos@222: names.sort(key=hash) bos@222: for d in dirs[:len(dirs)/2]: bos@222: inst.add([d]) bos@222: inst.commit('Add %r' % d, [d]) bos@222: inst.add(dirs[len(dirs)/2:] + names) bos@222: inst.commit('Add remaining dirs and files') bos@222: finally: bos@222: print >> sys.stderr, '[cleaning up...]' bos@222: shutil.rmtree(root) bos@222: os.chdir(oldcwd) bos@222: bos@222: benchmark(mercurial) bos@222: #benchmark(subversion)