Index: bench/logs/minigc.log =================================================================== --- bench/logs/minigc.log (revision 0) +++ bench/logs/minigc.log (revision 0) @@ -0,0 +1,73 @@ +2011-05-10T17:23:22.253-0700: 1.589: [GC 1.589: [ParNew: 34112K->4222K(38336K), 0.0512823 secs] 34112K->25292K(1019776K), 0.0513774 secs] [Times: user=0.03 sys=0.03, real=0.05 secs] +2011-05-10T17:23:23.143-0700: 2.479: [GC 2.479: [ParNew: 38334K->4224K(38336K), 0.0446728 secs] 59404K->58092K(1019776K), 0.0447375 secs] [Times: user=0.03 sys=0.04, real=0.04 secs] +2011-05-10T17:23:24.015-0700: 3.351: [GC 3.351: [ParNew: 38336K->4224K(38336K), 0.0448738 secs] 92204K->91367K(1019776K), 0.0449372 secs] [Times: user=0.03 sys=0.04, real=0.05 secs] +2011-05-10T17:23:24.932-0700: 4.268: [GC 4.268: [ParNew: 38336K->4224K(38336K), 0.0438131 secs] 125479K->124966K(1019776K), 0.0438723 secs] [Times: user=0.04 sys=0.03, real=0.04 secs] +2011-05-10T17:23:25.789-0700: 5.125: [GC 5.125: [ParNew: 38336K->4224K(38336K), 0.0473583 secs] 159078K->158059K(1019776K), 0.0474184 secs] [Times: user=0.03 sys=0.03, real=0.05 secs] +2011-05-10T17:23:26.698-0700: 6.034: [GC 6.034: [ParNew: 38336K->4224K(38336K), 0.0425643 secs] 192171K->191541K(1019776K), 0.0426256 secs] [Times: user=0.04 sys=0.03, real=0.05 secs] +2011-05-10T17:23:27.603-0700: 6.939: [GC 6.939: [ParNew: 38336K->4224K(38336K), 0.0428831 secs] 225653K->224835K(1019776K), 0.0429479 secs] [Times: user=0.04 sys=0.04, real=0.04 secs] +2011-05-10T17:23:28.456-0700: 7.793: [GC 7.793: [ParNew: 38336K->4224K(38336K), 0.0468046 secs] 258947K->258242K(1019776K), 0.0468895 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:29.364-0700: 8.700: [GC 8.700: [ParNew: 38336K->4224K(38336K), 0.0513369 secs] 292354K->291686K(1019776K), 0.0514267 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:30.231-0700: 9.567: [GC 9.567: [ParNew: 38336K->4224K(38336K), 0.0429645 secs] 325798K->325046K(1019776K), 0.0430252 secs] [Times: user=0.03 sys=0.03, real=0.04 secs] +2011-05-10T17:23:31.133-0700: 10.470: [GC 10.470: [ParNew: 38336K->4224K(38336K), 0.0432844 secs] 359158K->358492K(1019776K), 0.0433464 secs] [Times: user=0.04 sys=0.04, real=0.04 secs] +2011-05-10T17:23:31.997-0700: 11.334: [GC 11.334: [ParNew: 38336K->4224K(38336K), 0.0641123 secs] 392604K->391891K(1019776K), 0.0641794 secs] [Times: user=0.04 sys=0.04, real=0.07 secs] +2011-05-10T17:23:32.875-0700: 12.211: [GC 12.211: [ParNew: 38336K->4224K(38336K), 0.0533747 secs] 426003K->425139K(1019776K), 0.0534353 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:33.788-0700: 13.124: [GC 13.124: [ParNew: 38336K->4224K(38336K), 0.0507278 secs] 459251K->458536K(1019776K), 0.0507896 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:34.655-0700: 13.991: [GC 13.992: [ParNew: 38336K->4224K(38336K), 0.0745291 secs] 492648K->491825K(1019776K), 0.0746291 secs] [Times: user=0.04 sys=0.03, real=0.07 secs] +2011-05-10T17:23:35.541-0700: 14.878: [GC 14.878: [ParNew: 38333K->4224K(38336K), 0.0489466 secs] 525935K->525042K(1019776K), 0.0490129 secs] [Times: user=0.04 sys=0.03, real=0.05 secs] +2011-05-10T17:23:35.592-0700: 14.928: [GC [1 CMS-initial-mark: 520818K(981440K)] 526271K(1019776K), 0.0010942 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:23:35.593-0700: 14.930: [CMS-concurrent-mark-start] +2011-05-10T17:23:35.660-0700: 14.997: [CMS-concurrent-mark: 0.067/0.067 secs] [Times: user=0.05 sys=0.02, real=0.07 secs] +2011-05-10T17:23:35.660-0700: 14.997: [CMS-concurrent-preclean-start] +2011-05-10T17:23:35.662-0700: 14.999: [CMS-concurrent-preclean: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:23:35.662-0700: 14.999: [CMS-concurrent-abortable-preclean-start] +2011-05-10T17:23:36.086-0700: 15.423: [CMS-concurrent-abortable-preclean: 0.016/0.424 secs] [Times: user=0.03 sys=0.00, real=0.42 secs] +2011-05-10T17:23:36.092-0700: 15.429: [GC[YG occupancy: 22859 K (38336 K)]15.429: [Rescan (parallel) , 0.0013700 secs]15.430: [weak refs processing, 0.0000424 secs] [1 CMS-remark: 520818K(981440K)] 543678K(1019776K), 0.0014815 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:23:36.100-0700: 15.437: [CMS-concurrent-sweep-start] +2011-05-10T17:23:36.118-0700: 15.455: [CMS-concurrent-sweep: 0.018/0.018 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] +2011-05-10T17:23:36.118-0700: 15.455: [CMS-concurrent-reset-start] +2011-05-10T17:23:36.123-0700: 15.459: [CMS-concurrent-reset: 0.004/0.004 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] +2011-05-10T17:23:36.461-0700: 15.798: [GC 15.798: [ParNew: 38336K->4224K(38336K), 0.0858052 secs] 559152K->557640K(1019776K), 0.0858760 secs] [Times: user=0.04 sys=0.03, real=0.08 secs] +2011-05-10T17:23:37.411-0700: 16.748: [GC 16.748: [ParNew: 38336K->4224K(38336K), 0.0471120 secs] 591752K->590708K(1019776K), 0.0471822 secs] [Times: user=0.04 sys=0.03, real=0.04 secs] +2011-05-10T17:23:38.320-0700: 17.657: [GC 17.657: [ParNew: 38336K->4224K(38336K), 0.0577981 secs] 624820K->623946K(1019776K), 0.0578600 secs] [Times: user=0.04 sys=0.03, real=0.05 secs] +2011-05-10T17:23:39.189-0700: 18.526: [GC 18.526: [ParNew: 38336K->4224K(38336K), 0.0500779 secs] 658058K->657244K(1019776K), 0.0501347 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:40.061-0700: 19.398: [GC 19.398: [ParNew: 38329K->4224K(38336K), 0.0696710 secs] 691350K->690510K(1019776K), 0.0697347 secs] [Times: user=0.04 sys=0.03, real=0.07 secs] +2011-05-10T17:23:40.992-0700: 20.330: [GC 20.330: [ParNew: 38336K->4224K(38336K), 0.0473133 secs] 724622K->723977K(1019776K), 0.0473821 secs] [Times: user=0.04 sys=0.04, real=0.05 secs] +2011-05-10T17:23:41.851-0700: 21.188: [GC 21.188: [ParNew: 38336K->4224K(38336K), 0.0475485 secs] 758089K->757276K(1019776K), 0.0476111 secs] [Times: user=0.05 sys=0.03, real=0.04 secs] +2011-05-10T17:23:42.762-0700: 22.099: [GC 22.099: [ParNew: 38333K->4224K(38336K), 1.6661306 secs] 791385K->790683K(1019776K), 1.6677627 secs] [Times: user=0.05 sys=0.14, real=1.67 secs] +2011-05-10T17:23:45.265-0700: 24.603: [GC 24.603: [ParNew: 38336K->4224K(38336K), 1.0158994 secs] 824795K->823524K(1019776K), 1.0159563 secs] [Times: user=0.04 sys=0.11, real=1.02 secs] +2011-05-10T17:23:47.098-0700: 26.436: [GC 26.436: [ParNew: 38336K->4224K(38336K), 0.4825417 secs] 857636K->856472K(1019776K), 0.4826022 secs] [Times: user=0.04 sys=0.05, real=0.49 secs] +2011-05-10T17:23:48.395-0700: 27.732: [GC 27.733: [ParNew: 38336K->4224K(38336K), 0.8832201 secs] 890584K->889441K(1019776K), 0.8832878 secs] [Times: user=0.05 sys=0.04, real=0.88 secs] +2011-05-10T17:23:50.140-0700: 29.477: [GC 29.477: [ParNew: 38336K->4224K(38336K), 0.5555780 secs] 923553K->922568K(1019776K), 0.5591243 secs] [Times: user=0.04 sys=0.05, real=0.55 secs] +2011-05-10T17:23:50.741-0700: 30.079: [GC [1 CMS-initial-mark: 918344K(981440K)] 923962K(1019776K), 0.0005936 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:23:50.742-0700: 30.080: [CMS-concurrent-mark-start] +2011-05-10T17:23:51.562-0700: 30.899: [GC 30.899: [ParNew: 38336K->4224K(38336K), 0.5126136 secs] 956680K->955847K(1019776K), 0.5129189 secs] [Times: user=0.05 sys=0.05, real=0.51 secs] +2011-05-10T17:23:52.912-0700: 32.250: [GC 32.250: [ParNew: 38336K->38336K(38336K), 0.0000165 secs]32.250: [CMS2011-05-10T17:23:55.601-0700: 34.939: [CMS-concurrent-mark: 4.345/4.859 secs] [Times: user=0.20 sys=1.27, real=4.86 secs] + (concurrent mode failure): 951623K->981437K(981440K), 11.2165749 secs] 989959K->988810K(1019776K), [CMS Perm : 8186K->8183K(21248K)], 11.2189945 secs] [Times: user=0.81 sys=3.97, real=11.22 secs] +2011-05-10T17:24:04.905-0700: 44.244: [Full GC 44.244: [CMS: 981437K->901033K(981440K), 0.8521745 secs] 1019765K->901033K(1019776K), [CMS Perm : 8222K->8204K(21248K)], 0.8522540 secs] [Times: user=0.80 sys=0.02, real=0.85 secs] +2011-05-10T17:24:06.569-0700: 45.907: [GC 45.907: [ParNew: 34106K->4224K(38336K), 0.0366859 secs] 935140K->934431K(1019776K), 0.0367496 secs] [Times: user=0.05 sys=0.00, real=0.04 secs] +2011-05-10T17:24:06.605-0700: 45.944: [GC [1 CMS-initial-mark: 930207K(981440K)] 934677K(1019776K), 0.0003706 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:06.606-0700: 45.945: [CMS-concurrent-mark-start] +2011-05-10T17:24:06.685-0700: 46.024: [CMS-concurrent-mark: 0.080/0.080 secs] [Times: user=0.09 sys=0.00, real=0.08 secs] +2011-05-10T17:24:06.685-0700: 46.024: [CMS-concurrent-preclean-start] +2011-05-10T17:24:06.690-0700: 46.029: [CMS-concurrent-preclean: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] +2011-05-10T17:24:06.691-0700: 46.029: [CMS-concurrent-abortable-preclean-start] +2011-05-10T17:24:07.427-0700: 46.766: [GC 46.766: [ParNew: 38334K->4224K(38336K), 0.0478180 secs] 968541K->967547K(1019776K), 0.0478835 secs] [Times: user=0.05 sys=0.01, real=0.05 secs] +2011-05-10T17:24:07.905-0700: 47.244: [CMS-concurrent-abortable-preclean: 0.061/1.214 secs] [Times: user=0.14 sys=0.01, real=1.21 secs] +2011-05-10T17:24:07.905-0700: 47.244: [GC[YG occupancy: 22038 K (38336 K)]47.244: [Rescan (parallel) , 0.0018911 secs]47.246: [weak refs processing, 0.0000053 secs] [1 CMS-remark: 963323K(981440K)] 985362K(1019776K), 0.0019578 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:07.907-0700: 47.246: [CMS-concurrent-sweep-start] +2011-05-10T17:24:07.934-0700: 47.273: [CMS-concurrent-sweep: 0.027/0.027 secs] [Times: user=0.03 sys=0.01, real=0.03 secs] +2011-05-10T17:24:07.934-0700: 47.273: [CMS-concurrent-reset-start] +2011-05-10T17:24:07.937-0700: 47.276: [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:08.348-0700: 47.687: [GC 47.687: [ParNew: 38336K->38336K(38336K), 0.0000219 secs]47.687: [CMS: 963323K->882821K(981440K), 0.8211142 secs] 1001659K->882821K(1019776K), [CMS Perm : 8204K->8204K(21248K)], 0.8212581 secs] [Times: user=0.78 sys=0.01, real=0.83 secs] +2011-05-10T17:24:09.981-0700: 49.320: [GC 49.320: [ParNew: 34112K->4224K(38336K), 0.0463879 secs] 916933K->916078K(1019776K), 0.0464535 secs] [Times: user=0.04 sys=0.00, real=0.04 secs] +2011-05-10T17:24:10.027-0700: 49.367: [GC [1 CMS-initial-mark: 911854K(981440K)] 916087K(1019776K), 0.0004516 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:10.028-0700: 49.367: [CMS-concurrent-mark-start] +2011-05-10T17:24:10.111-0700: 49.450: [CMS-concurrent-mark: 0.083/0.083 secs] [Times: user=0.09 sys=0.00, real=0.09 secs] +2011-05-10T17:24:10.111-0700: 49.450: [CMS-concurrent-preclean-start] +2011-05-10T17:24:10.114-0700: 49.453: [CMS-concurrent-preclean: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:10.114-0700: 49.453: [CMS-concurrent-abortable-preclean-start] +2011-05-10T17:24:10.912-0700: 50.252: [GC 50.252: [ParNew: 38336K->4224K(38336K), 0.0475511 secs] 950190K->949568K(1019776K), 0.0476116 secs] [Times: user=0.07 sys=0.00, real=0.05 secs] +2011-05-10T17:24:11.412-0700: 50.751: [CMS-concurrent-abortable-preclean: 0.082/1.298 secs] [Times: user=0.16 sys=0.01, real=1.30 secs] +2011-05-10T17:24:11.412-0700: 50.751: [GC[YG occupancy: 22571 K (38336 K)]50.751: [Rescan (parallel) , 0.0017195 secs]50.753: [weak refs processing, 0.0000057 secs] [1 CMS-remark: 945344K(981440K)] 967916K(1019776K), 0.0017851 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] +2011-05-10T17:24:11.414-0700: 50.753: [CMS-concurrent-sweep-start] +2011-05-10T17:24:11.439-0700: 50.779: [CMS-concurrent-sweep: 0.026/0.026 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] Index: bench/analyzeGC.py =================================================================== --- bench/analyzeGC.py (revision 0) +++ bench/analyzeGC.py (revision 0) @@ -0,0 +1,547 @@ +#!/usr/bin/python + +import re +import sys + +# Constants + +DEBUG = False + +# Regex Helpers + +def timeHead(prefix): + return '(?P<' + prefix + 'Date>\d+-\d+-\d+T\d+:\d+:\d+\.\d+-\d+): (?P<' + prefix + 'Time>\d+.\d+): ' + +def timeTail(prefix): + return "\[Times: user=(?P<" + prefix + "UserTime>\d+.\d+) sys=(?P<" + prefix + "SysTime>\d+.\d+), real=(?P<" + prefix + "RealTime>\d+.\d+) secs\]" + +def makeGroup(phaseName): + return phaseName.replace('-','') + +# Methods + +def parseLog(filename): + + parser = GarbageCollectionLogParser(filename) + parser.parse() + return parser.getResult() + +# Parsing class + +class GarbageCollectionLogParser(): + """Log parser and processor""" + + def __init__(self, filename): + print 'Constructing log parser for file: ' + filename + self.stream = open(filename, 'r') + + self.regexList = [ + MinorGarbageCollection.regexString(), + FullGarbageCollection.regexString(), + CMSFailureGarbageCollection.regexString(), + BadLineGarbageCollection.regexString(), + CMSGarbageCollection.regexString()] + + self.regexString = '(' + ')|('.join(self.regexList) + ')' + self.regex = re.compile(self.regexString) + self.cmsRegexString = CMSPhase.regexString() + self.cmsRegex = re.compile(self.cmsRegexString) + + def parse(self): + self.gcs = [] + self.unmatched = [] + self.lines = [] + self.gcdict = {'minor':[],'full':[],'bad':[],'fail':[],'cms':[]} + currentCMS = None + # Iterate lines of GC log + for line in self.stream: + self.lines.append(line) + # Perform regex + match = self.regex.search(line) + if match is None: + # Regex did not match + self.unmatched.append(line) + continue + # Regex matched one of the known GC lines + result = match.groupdict() + # Check for different types + if result['minor'] is not None: + gc = MinorGarbageCollection(line, result) + self.gcs.append(gc) + self.gcdict['minor'].append(gc) + elif result['full'] is not None: + gc = FullGarbageCollection(line, result) + self.gcs.append(gc) + self.gcdict['full'].append(gc) + elif result['bad'] is not None: + gc = BadLineGarbageCollection(line, result) + self.gcs.append(gc) + self.gcdict['bad'].append(gc) + elif result['fail'] is not None: + result['failTime'] = self.gcs[len(self.gcs)-1].startTime + result['failDate'] = self.gcs[len(self.gcs)-1].startDate + gc = CMSFailureGarbageCollection(line, result) + self.gcs.append(gc) + self.gcdict['fail'].append(gc) + elif result['cms'] is not None: + if DEBUG: print 'FOUND CMS' + if DEBUG: print line + result = self.cmsRegex.search(line).groupdict() + cmsPhase = CMSPhase(line, result) + # Valid states: (INIT + NULL) | (INIT + !NULL + FINISHED) | (!INIT + !NULL + PRIOR_STATE) + # If we aren't in a CMS already, this must be an INIT + if currentCMS is None: + if cmsPhase.phase is CMSPhase.PHASE_MAP['init']: + currentCMS = CMSGarbageCollection(cmsPhase) + self.gcs.append(currentCMS) + self.gcdict['cms'].append(currentCMS) + continue + else: + if DEBUG: print 'Found a CMS phase before finding a CMS init' + exit + # We are already in a CMS, verify we are now at an expected state + if DEBUG: print 'predecessor: ' + str(cmsPhase.getPredecessor()) + ', previous: ' + str(currentCMS.currentPhase.phase) + ', current: ' + str(cmsPhase.phase) + if cmsPhase.getPredecessor() is currentCMS.currentPhase.phase: + # Expected state + if DEBUG: print 'EXPECTED CMS STATE' + if cmsPhase.phase is CMSPhase.PHASE_MAP['init']: + currentCMS = CMSGarbageCollection(cmsPhase) + self.gcs.append(currentCMS) + self.gcdict['cms'].append(currentCMS) + if DEBUG: print 'Start of CMS' + else: + currentCMS.setCurrentPhase(cmsPhase) + if DEBUG: print 'New CMS state' +# elif cmsPhase.phase is CMSPhase.PHASE_MAP['fail']: +# # CMS Failure +# if DEBUG: print 'CMS Failure!!!' +# currentCMS.setCurrentPhase(cmsPhase) +# currentCMS = None + else: + if DEBUG: print 'Invalid CMS state. Current: ' + cmsPhase.toStr() + ', Expected Predecessor: ' + CMSPhase.PHASES[cmsPhase.getPredecessor()] + ', Actual Predecessor: ' + currentCMS.currentPhase.toStr() + exit + else: + self.unmatched.append(line) + + def getResult(self): + return {'totalLines':len(self.lines), 'unmatched':self.unmatched, 'matched':self.gcs, 'gctypes':self.gcdict} + +# Classes + +class GarbageCollection: + """Information about a single Garbage Collection""" + + def __init__(self, line, startDate, startTime, totalUserTime, totalSysTime, totalRealTime): + self.logLine = line + self.startDate = startDate + self.startTime = startTime + self.totalTime = {'user':totalUserTime, 'sys':totalSysTime, 'real':totalRealTime} + + def toStr(self): + return 'startDate=' + self.startDate + ', startTime=' + self.startTime + ', totalTime=' + str(self.totalTime) + +class MinorGarbageCollection(GarbageCollection): + """Information about a single Minor Garbage Collection""" + + def __init__(self, line, result): + self.result = result + GarbageCollection.__init__(self, line, result['minorDate'], result['minorTime'], result['minorUserTime'], result['minorSysTime'], result['minorRealTime']) + + def toStr(self): + return 'Minor GC: ' + GarbageCollection.toStr(self) + + @staticmethod + def regexString(): + return timeHead('minor') + \ + '(?P' \ + '\[GC \d+\.\d+: \[ParNew: (?P\d+)K->(?P\d+)K\((?P\d+)K\), (?P\d+.\d+) secs\] ' \ + '(?P\d+)K->(?P\d+)K\((?P\d+)K\), (?P\d+.\d+) secs\] ' + \ + timeTail('minor') + \ + ')' + +class FullGarbageCollection(GarbageCollection): + """Information about a single Full Garbage Collection""" + + def __init__(self, line, result): + self.result = result + GarbageCollection.__init__(self, line, result['fullDate'], result['fullTime'], result['fullUserTime'], result['fullSysTime'], result['fullRealTime']) + + def toStr(self): + return 'Full GC: ' + GarbageCollection.toStr(self) + + @staticmethod + def regexString(): + return timeHead('full') + \ + '(?P' \ + '\[Full GC \d+\.\d+: \[CMS: (?P\d+)K->(?P\d+)K\((?P\d+)K\), (?P\d+.\d+) secs\] ' \ + '(?P\d+)K->(?P\d+)K\((?P\d+)K\), ' \ + '\[CMS Perm : (?P\d+)K->(?P\d+)K\((?P\d+)K\)\], (?P\d+.\d+) secs\] ' + \ + timeTail('full') + \ + ')' + +class BadLineGarbageCollection(GarbageCollection): + """Information about a bad/mangled Garbage Collection (contains more than two start times, usually before a CMS failure)""" + + def __init__(self, line, result): + self.result = result + GarbageCollection.__init__(self, line, result['badDate'], result['badTime'], 0, 0, 0) + + def toStr(self): + return 'Bad GC: ' + self.logLine + + @staticmethod + def regexString(): + return timeHead('bad') + \ + '(?P.* \d+\.\d+: .*\d+\.\d+: .*\d+\.\d+ .*)' + +class CMSGarbageCollection(GarbageCollection): + """Information about a Concurrent MarkAndSweep Garbage Collection""" + + def __init__(self, initPhase): + self.phases = [initPhase] + self.currentPhase = initPhase + + def toStr(self): + return 'CMS GC: ' + str([x.toStr() + ' , ' for x in self.phases]) + + def setCurrentPhase(self, latestPhase): + self.phases.append(latestPhase) + self.currentPhase = latestPhase + + def isComplete(self): + if self.currentPhase.phase is CMSPhase.PHASE_MAP['reset']: + return True + return False + + def totalTime(self): + return str(float(self.phases[len(self.phases)-1].startTime) - float(self.phases[0].startTime)) + + def totalCollected(self): + return str(0) + + @staticmethod + def regexString(): + return timeHead('cms') + \ + '(?P\[.*CMS.*\])' + +class CMSFailureGarbageCollection(GarbageCollection): + """Information about a CMS Failure""" + + def __init__(self, line, result): + self.result = result + GarbageCollection.__init__(self, line, result['failDate'], result['failTime'], result['failUserTime'], result['failSysTime'], result['failRealTime']) + + def toStr(self): + return 'CMS Failure: ' + self.logLine + + @staticmethod + def regexString(): + return '(?P.*\(concurrent mode failure\).*' + timeTail('fail') + ')' + +class CMSPhase(GarbageCollection): + """Information about a single phase of a CMS Garbage Collection""" + + # Phases + PHASES = ['init', 'mark-start', 'mark', 'preclean-start', 'preclean', 'abortable-preclean-start', 'abortable-preclean', 'rescan', 'sweep-start', 'sweep', 'reset-start', 'reset'] + PHASE_MAP = dict([(phaseName, phaseIndex) for phaseIndex, phaseName in enumerate(PHASES)]) + + def __init__(self, line, result): + self.phase = None + for (i,p) in enumerate(CMSPhase.PHASES): + groupName = p.replace('-','') + if result.has_key(groupName) and result[groupName] is not None: + self.phase = i + break + if self.phase is None: + print 'Unable to find CMS phase match for line: ' + line + exit(0) + GarbageCollection.__init__(self, line, result.get(makeGroup(CMSPhase.PHASES[self.phase])+'Date', 0), \ + result.get(makeGroup(CMSPhase.PHASES[self.phase])+'Time', 0), result.get(makeGroup(CMSPhase.PHASES[self.phase])+'UserTime', 0), \ + result.get(makeGroup(CMSPhase.PHASES[self.phase])+'SysTime', 0), result.get(makeGroup(CMSPhase.PHASES[self.phase])+'RealTime', 0)) + + def getPredecessor(self): + if self.phase is 0: + return len(CMSPhase.PHASES) - 1 + return self.phase - 1 + + def toStr(self): + return 'CMS-' + CMSPhase.PHASES[self.phase] + ': ' + GarbageCollection.toStr(self) + + @staticmethod + def cmsRegexInit(): + return timeHead('init') + \ + '(?P' \ + '\[GC \[1 CMS-initial-mark: (?P\d+)K\((?P\d+)K\)\] \d+K\(\d+K\), (?P\d+.\d+) secs\] ' + \ + timeTail('init') + \ + ')' + + @staticmethod + def cmsRegexStart(phaseName): + phaseName = phaseName + '-start' + groupName = phaseName.replace('-','') + return timeHead(groupName) + \ + '(?P<' + groupName + '>' \ + '\[CMS-concurrent-' + phaseName + '\])' + + @staticmethod + def cmsRegex(phaseName): + groupName = phaseName.replace('-','') + return timeHead(groupName) + \ + '(?P<' + groupName + '>' \ + '\[CMS-concurrent-' + phaseName + ': (?P<' + groupName + 'TimeOne>\d+.\d+)\/(?P<' + groupName + 'TimeTwo>\d+.\d+) secs\] ' + \ + timeTail(groupName) + \ + ')' + + @staticmethod + def cmsRegexRescan(): + return timeHead('rescan') + \ + '(?P\[GC\[YG.+\] ' + \ + timeTail('rescan') + \ + ')' + + @staticmethod + def cmsFailure(): + return '(?P.*\(concurrent mode failure\).*' + \ + timeTail('fail') + \ + ')' + + @staticmethod + def regexString(): + return '(' + str.join(')|(', \ + [ CMSPhase.cmsRegexInit(), CMSPhase.cmsRegexStart('mark'), CMSPhase.cmsRegex('mark'), CMSPhase.cmsRegexStart('preclean'), CMSPhase.cmsRegex('preclean'), \ + CMSPhase.cmsRegexStart('abortable-preclean'), CMSPhase.cmsRegex('abortable-preclean'), CMSPhase.cmsRegexRescan(), \ + CMSPhase.cmsRegexStart('sweep'), CMSPhase.cmsRegex('sweep'), CMSPhase.cmsRegexStart('reset'), CMSPhase.cmsRegex('reset'), \ + CMSPhase.cmsFailure() ]) + \ + ')' + +# Summarizing + +divider = '---' + +def printSummary(result): + + gcs = result['gctypes'] + gcs['all'] = result['matched'] + numgcs = len(gcs['all']) + startDate = gcs['all'][0].startDate + startTime = float(gcs['all'][0].startTime) + endDate = gcs['all'][numgcs-1].startDate + endTime = float(gcs['all'][numgcs-1].startTime) + totalTime = endTime - startTime + + # High-level summary + + print divider + print 'Parsed ' + str(result['totalLines']) + ' GC log lines into ' + str(len(gcs['all'])) + ' valid collections and ' + str(len(result['unmatched'])) + ' unknown lines' + + + print + print 'Log covered ' + str(totalTime) + ' seconds between ' + startDate + ' and ' + endDate + + # Collection type counts + + print + print 'Minor GCs: ' + str(len(gcs['minor'])) + print 'Full GCs: ' + str(len(gcs['full'])) + print 'CMS GCs: ' + str(len(gcs['cms'])) + print 'CMS Failures: ' + str(len(gcs['fail'])) + print 'Bad Lines: ' + str(len(gcs['bad'])) + + # Per-type summary + + print + printMinorSummary(gcs['minor'], totalTime) + print + printFullSummary(gcs['full'], totalTime) + print + printCMSSummary(gcs['cms'], totalTime) + print + printFailureSummary(gcs['fail'], totalTime) + print + printBadSummary(gcs['bad'], totalTime) + print + +def calculateTimes(gcs): + totalUser = float(0) + totalSys = float(0) + totalReal = float(0) + count = len(gcs) + for gc in gcs: + totalUser += float(gc.totalTime['user']) + totalSys += float(gc.totalTime['sys']) + totalReal += float(gc.totalTime['real']) + return {'totalUser':totalUser, 'totalSys':totalSys, 'totalReal':totalReal, 'avgUser':totalUser / count, 'avgSys':totalSys / count, 'avgReal':totalReal / count} + +def printTimes(times, totalTime): + print 'Collection Times' + print 'User: Total=' + str(times['totalUser']) + ', Average=' + str(times['avgUser']) + print 'Sys: Total=' + str(times['totalSys']) + ', Average=' + str(times['avgSys']) + print 'Real: Total=' + str(times['totalReal']) + ', Average=' + str(times['avgReal']) + ', Percentage=' + str(100*(times['totalReal']/totalTime)) + '% of all real time' + print + +def calculateMinorGarbageCollected(gcs): + parNewTotal = int(gcs[0].result['minorNewTotal']) + parNewCreated = 0 + parNewCollected = 0 + previousStart = 0 + for gc in gcs: + parNewCreated += (int(gc.result['minorNewStart']) - previousStart) + parNewCollected += (int(gc.result['minorNewStart']) - int(gc.result['minorNewEnd'])) + previousStart = int(gc.result['minorNewEnd']) + return {'count':len(gcs), 'created':parNewCreated, 'collected':parNewCollected, 'total':parNewTotal} + +def printMinorCollected(collected, totalTime): + print 'ParNew Size: ' + prettyBytes(collected['total']) + print 'Garbage Created: ' + prettyBytes(collected['created']) + ' in ' + prettyTime(totalTime) + ', ' + prettyRate(collected['created'], totalTime, 'sec') + print 'Garbage Collected: ' + prettyBytes(collected['collected']) + ' in ' + prettyTime(totalTime) + ', ' + prettyRate(collected['collected'], collected['count'], 'minor collection') + print 'Percentage Collected: ' + str(((collected['collected'] / collected['count']) / float(collected['total']))*100) + '% of ParNew collected each time on average' + +def calculateFullGarbageCollected(gcs): + oldTotal = int(gcs[0].result['fullCMSTotal']) + oldCreated = 0 + oldCollected = 0 + previousStart = 0 + for gc in gcs: + oldCreated += (int(gc.result['fullCMSStart']) - previousStart) + oldCollected += (int(gc.result['fullCMSStart']) - int(gc.result['fullCMSEnd'])) + previousStart = int(gc.result['fullCMSEnd']) + return {'count':len(gcs), 'created':oldCreated, 'collected':oldCollected, 'total':oldTotal} + +def printFullCollected(collected, totalTime): + print 'Old Gen Size: ' + prettyBytes(collected['total']) +# print 'Garbage Created: ' + prettyBytes(collected['created']) + ' in ' + prettyTime(totalTime) + ', ' + prettyRate(collected['created'], totalTime, 'sec') + print 'Garbage Collected: ' + prettyBytes(collected['collected']) + ' in ' + prettyTime(totalTime) + ', ' + prettyRate(collected['collected'], collected['count'], 'minor collection') + +def prettyBytes(bytes): + bytes = bytes * 1024 + if bytes < 10 * 1024: + return str(bytes) + ' bytes' + elif bytes < 10 * 1024 * 1024: + return str(bytes / 1024) + ' KB' + elif bytes < 10 * 1024 * 1024 * 1024: + return str(bytes / (1024*1024)) + ' MB' + else: + return str(bytes / (1024*1024*1024)) + ' GB' + +def prettyTime(totalTime): + if totalTime < 1: + return str(totalTime * 1000) + ' milliseconds' + else: + return str(totalTime) + ' seconds' + +def prettyRate(bytes, count, unit): + bytes = prettyBytes(bytes / count) + return bytes + '/' + unit + +def printMinorSummary(gcs, totalTime): + numgcs = len(gcs) + + print 'Minor Garbage Collections (' + str(numgcs) + ')' + print divider + + times = calculateTimes(gcs) + printTimes(times, totalTime) + + collected = calculateMinorGarbageCollected(gcs) + printMinorCollected(collected, totalTime) + print + +def printFullSummary(gcs, totalTime): + numgcs = len(gcs) + + print 'Full Garbage Collections (' + str(numgcs) + ')' + print divider + + if numgcs is 0: + return + + times = calculateTimes(gcs) + printTimes(times, totalTime) + + collected = calculateFullGarbageCollected(gcs) + printFullCollected(collected, totalTime) + print + +def printCMSSummary(gcs, totalTime): + numgcs = len(gcs) + + if numgcs is 0: + return + + complete = [] + failed = [] + for gc in gcs: + if gc.isComplete(): + complete.append(gc) + else: + failed.append(gc) + print 'CMS Garbage Collections (' + str(numgcs) + ', completed=' + str(len(complete)) + ', failed=' + str(len(failed)) + ')' + print divider + +# times = calculateCMSTimes(complete) +# printCMSTimes(times, totalTime) + + print 'Completed CMS Collections:' + for gc in complete: + print 'CMS: TotalTime=' + gc.totalTime() + ', TotalCollected=' + gc.totalCollected() + + print 'Failed CMS Collections:' + for gc in failed: + print 'CMS: TotalTime=' + gc.totalTime() + ', TotalCollected=' + gc.totalCollected() + + print + +def printFailureSummary(gcs, totalTime): + numgcs = len(gcs) + + print 'Failed Garbage Collections (' + str(numgcs) + ')' + print divider + + if numgcs is 0: + return + + times = calculateTimes(gcs) + printTimes(times, totalTime) + +# collected = calculateFullGarbageCollected(gcs) +# printFullCollected(collected, totalTime) + +def printBadSummary(gcs, totalTime): + numgcs = len(gcs) + + print 'Bad Garbage Collections (' + str(numgcs) + ')' + print divider + + if numgcs is 0: + return + + for gc in gcs: + print gc.logLine + +# times = calculateTimes(gcs) +# printTimes(times, totalTime) + +# collected = calculateFullGarbageCollected(gcs) +# printFullCollected(collected, totalTime) + + + +# Main + +def main(argv=None): + + if len(argv) < 2: + print 'Filename required' + return {'returnCode':0} + + filename = argv[1] + + parseResult = parseLog(filename) + + printSummary(parseResult) + + parseResult['returnCode'] = 1 + return parseResult + +if __name__ == "__main__": + sys.exit(main(sys.argv)['returnCode']) Index: bench/testGCAnalyzer.py =================================================================== --- bench/testGCAnalyzer.py (revision 0) +++ bench/testGCAnalyzer.py (revision 0) @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import analyzeGC + +import unittest + +class TestGCAnalyzer(unittest.TestCase): + + def setUp(self): + pass + + def test_gc_minilog(self): + # test the parsing of the sample minilog + result = analyzeGC.main(['analyzeGCUnitTest', 'logs/minigc.log']) + self.assertTrue(result['returnCode'] == 1) + self.assertTrue(result['totalLines'] == 73) + self.assertTrue(len(result['matched']) == 34) + self.assertTrue(len(result['unmatched']) == 39) + +if __name__ == '__main__': + unittest.main() Index: bench/runMemBench.sh =================================================================== --- bench/runMemBench.sh (revision 0) +++ bench/runMemBench.sh (revision 0) @@ -0,0 +1,14 @@ +#!/bin/bash + +RUN_NAME=`date "+%m-%d-%Y_%H%M%S"` +HBASE_HOME=`pwd` +HBASE_LOG=$HBASE_HOME/logs/memBench-hbase-$RUN_NAME.log +GC_LOG=$HBASE_HOME/logs/memBench-gc-$RUN_NAME.log +M2_REPO=${M2_REPO:-~/.m2} +HBASE_CLASSPATH=$HBASE_HOME/conf:/usr/local/jdk-6u7-64/lib/tools.jar:$M2_REPO/repository/ant/ant/1.6.5/ant-1.6.5.jar:$M2_REPO/repository/asm/asm/3.1/asm-3.1.jar:$M2_REPO/repository/com/google/guava/guava/r06/guava-r06.jar:$M2_REPO/repository/com/google/protobuf/protobuf-java/2.3.0/protobuf-java-2.3.0.jar:$M2_REPO/repository/com/sun/jersey/jersey-core/1.4/jersey-core-1.4.jar:$M2_REPO/repository/com/sun/jersey/jersey-json/1.4/jersey-json-1.4.jar:$M2_REPO/repository/com/sun/jersey/jersey-server/1.4/jersey-server-1.4.jar:$M2_REPO/repository/com/sun/xml/bind/jaxb-impl/2.1.12/jaxb-impl-2.1.12.jar:$M2_REPO/repository/com/thoughtworks/paranamer/paranamer/2.2/paranamer-2.2.jar:$M2_REPO/repository/com/thoughtworks/paranamer/paranamer-ant/2.2/paranamer-ant-2.2.jar:$M2_REPO/repository/com/thoughtworks/paranamer/paranamer-generator/2.2/paranamer-generator-2.2.jar:$M2_REPO/repository/com/thoughtworks/qdox/qdox/1.10.1/qdox-1.10.1.jar:$M2_REPO/repository/commons-cli/commons-cli/1.2/commons-cli-1.2.jar:$M2_REPO/repository/commons-codec/commons-codec/1.4/commons-codec-1.4.jar:$M2_REPO/repository/commons-el/commons-el/1.0/commons-el-1.0.jar:$M2_REPO/repository/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar:$M2_REPO/repository/commons-lang/commons-lang/2.5/commons-lang-2.5.jar:$M2_REPO/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar:$M2_REPO/repository/javax/activation/activation/1.1/activation-1.1.jar:$M2_REPO/repository/javax/ws/rs/jsr311-api/1.1.1/jsr311-api-1.1.1.jar:$M2_REPO/repository/javax/xml/bind/jaxb-api/2.1/jaxb-api-2.1.jar:$M2_REPO/repository/jline/jline/0.9.94/jline-0.9.94.jar:$M2_REPO/repository/junit/junit/4.8.1/junit-4.8.1.jar:$M2_REPO/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar:$M2_REPO/repository/org/apache/ant/ant/1.7.1/ant-1.7.1.jar:$M2_REPO/repository/org/apache/ant/ant-launcher/1.7.1/ant-launcher-1.7.1.jar:$M2_REPO/repository/org/apache/commons/commons-math/2.1/commons-math-2.1.jar:$M2_REPO/repository/org/apache/hadoop/avro/1.3.3/avro-1.3.3.jar:$M2_REPO/repository/org/apache/hadoop/hadoop-core/0.20/hadoop-core-0.20.jar:$M2_REPO/repository/org/apache/hadoop/hadoop-test/0.20/hadoop-test-0.20.jar:$M2_REPO/repository/org/apache/thrift/thrift/0.5.0/thrift-0.5.0.jar:$M2_REPO/repository/org/apache/zookeeper/zookeeper/3.3.2/zookeeper-3.3.2.jar:$M2_REPO/repository/org/codehaus/jackson/jackson-core-asl/1.5.5/jackson-core-asl-1.5.5.jar:$M2_REPO/repository/org/codehaus/jackson/jackson-jaxrs/1.5.5/jackson-jaxrs-1.5.5.jar:$M2_REPO/repository/org/codehaus/jackson/jackson-mapper-asl/1.4.2/jackson-mapper-asl-1.4.2.jar:$M2_REPO/repository/org/codehaus/jackson/jackson-xc/1.5.5/jackson-xc-1.5.5.jar:$M2_REPO/repository/org/codehaus/jettison/jettison/1.1/jettison-1.1.jar:$M2_REPO/repository/org/eclipse/jdt/core/3.1.1/core-3.1.1.jar:$M2_REPO/repository/org/jruby/jruby-complete/1.5.2/jruby-complete-1.5.2.jar:$M2_REPO/repository/org/mockito/mockito-all/1.8.5/mockito-all-1.8.5.jar:$M2_REPO/repository/org/mortbay/jetty/jetty/6.1.25/jetty-6.1.25.jar:$M2_REPO/repository/org/mortbay/jetty/jetty-util/6.1.25/jetty-util-6.1.25.jar:$M2_REPO/repository/org/mortbay/jetty/jsp-2.1/6.1.14/jsp-2.1-6.1.14.jar:$M2_REPO/repository/org/mortbay/jetty/jsp-api-2.1/6.1.14/jsp-api-2.1-6.1.14.jar:$M2_REPO/repository/org/mortbay/jetty/servlet-api-2.5/6.1.14/servlet-api-2.5-6.1.14.jar:$M2_REPO/repository/org/slf4j/slf4j-api/1.5.8/slf4j-api-1.5.8.jar:$M2_REPO/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar:$M2_REPO/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar:$M2_REPO/repository/tomcat/jasper-compiler/5.5.23/jasper-compiler-5.5.23.jar:$M2_REPO/repository/tomcat/jasper-runtime/5.5.23/jasper-runtime-5.5.23.jar:$HBASE_HOME/target/classes:$HBASE_HOME/target/test-classes:$HBASE_HOME/target:$HBASE_HOME/lib/*.jar +CLASSNAME=org.apache.hadoop.hbase.bench.BlockCacheBench +JAVA_HOME=${JAVA_HOME:-/usr/local/jdk-6u22-64} +JAVA_OPTS="$JAVA_OPTS -Xmx1000m -Xms1000m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseConcMarkSweepGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$GC_LOG" + +# Run it +$JAVA_HOME/bin/java $JAVA_OPTS -classpath $HBASE_HOME/target/classes:$HBASE_CLASSPATH $CLASSNAME $@ | tee $HBASE_LOG Index: src/test/java/org/apache/hadoop/hbase/bench/TestSimpleLRU.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/bench/TestSimpleLRU.java (revision 0) +++ src/test/java/org/apache/hadoop/hbase/bench/TestSimpleLRU.java (revision 0) @@ -0,0 +1,61 @@ +package org.apache.hadoop.hbase.bench; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TestSimpleLRU { + + @Test + public void testStupidLRU() throws Exception { + + SimpleLRU lru = new SimpleLRU(100, 2); + + lru.add("test1", "test1"); + assertNotNull(lru.get("test1")); + assertTrue(lru.head.name.equals("test1")); + assertNull(lru.tail); + + assertNull(lru.get("test2")); + lru.add("test2", "test2"); + assertTrue(lru.head.name.equals("test1")); + assertTrue(lru.tail.name.equals("test2")); + assertNotNull(lru.get("test2")); + assertTrue(lru.head.name.equals("test1")); + assertTrue(lru.tail.name.equals("test2")); + + lru.get("test1"); + assertTrue(lru.head.name.equals("test2")); + assertTrue(lru.tail.name.equals("test1")); + + assertNull(lru.get("test3")); + lru.add("test3", "test3"); + assertTrue(lru.head.name.equals("test2")); + assertTrue(lru.tail.name.equals("test3")); + assertNotNull(lru.get("test3")); + assertTrue(lru.head.name.equals("test2")); + assertTrue(lru.tail.name.equals("test3")); + + for (int i=0;i<97;i++) { + assertNull(lru.get("testA" + i)); + lru.add("testA" + i, "testA" + i); + assertNotNull(lru.get("testA" + i)); + } + + assertTrue(lru.map.size() == 100); + assertTrue(lru.size == 100); + + assertTrue(lru.map.containsKey("test1")); + assertTrue(lru.map.containsKey("test2")); + assertTrue(lru.map.containsKey("test3")); + lru.add("testB1", "testB1"); + assertFalse(lru.map.containsKey("test2")); + assertFalse(lru.map.containsKey("test1")); + assertTrue(lru.map.containsKey("test3")); + lru.add("testB2", "testB2"); + assertTrue(lru.map.containsKey("test3")); + lru.add("testB3", "testB3"); + assertFalse(lru.map.containsKey("test3")); + + } +} Index: src/main/java/org/apache/hadoop/hbase/bench/RandomGenerator.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/bench/RandomGenerator.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/bench/RandomGenerator.java (revision 0) @@ -0,0 +1,94 @@ +package org.apache.hadoop.hbase.bench; + +import java.util.Random; + +public class RandomGenerator { + + private static final Random r = new Random(); + + public static byte [] makeRandomBytes(int length) { + byte [] bytes = new byte[length]; + r.nextBytes(bytes); + return bytes; + } + + public static long gaussianLong(long mean, long stdev, long min, long max) { + double guess = (r.nextGaussian() * stdev) + mean; + if (guess < min || guess > max) return 0; + return (long)guess; + } + + private static boolean DEBUG = false; + + public static float runSim(int numBlocks, int blockSize, int maxSize, + int stdev, int iterations) { + int capacity = maxSize / blockSize; + int mean = numBlocks / 2; + if (DEBUG) + log("runSim; numBlocks=" + numBlocks + ", blockSize=" + blockSize + + ", maxSize=" + maxSize + ", capacity=" + capacity + ", mean=" + mean + + ", iterations=" + iterations + ", stdev=" + stdev); + + SimpleLRU lru = new SimpleLRU(capacity, capacity / 10); + + int hits = 0; + int misses = 0; + int zeroes = 0; + for (int i=0;i " + hitRatio + + "% hit ratio"); + + } + } +} Index: src/main/java/org/apache/hadoop/hbase/bench/SimpleLRU.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/bench/SimpleLRU.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/bench/SimpleLRU.java (revision 0) @@ -0,0 +1,157 @@ +package org.apache.hadoop.hbase.bench; + +import java.util.HashMap; + +/** + * As simple LRU backed by a hash table and linked list. + *

+ * You must provide your own synchronization as this class is not thread-safe. + */ +public class SimpleLRU { + + /** Maximum capacity of this LRU */ + final int capacity; + + /** The number of entries to clear per eviction */ + final int clearPerEviction; + + /** Hash table backing this LRU */ + final HashMap> map; + + /** Head of LRU (the least-recently-used entry) */ + Entry head; + + /** Tail of LRU (the most-recently-used entry) */ + Entry tail; + + /** Number of entries in LRU */ + int size; + + /** + * Construct a simple LRU with the specified maximum capacity and the + * specified number of entries to evict on each eviction. + * @param capacity + * @param clearPerEviciton + */ + public SimpleLRU(int capacity, int clearPerEviciton) { + this.capacity = capacity; + this.clearPerEviction = clearPerEviciton; + this.map = new HashMap>(); + this.head = null; + this.tail = null; + this.size = 0; + } + + /** + * Adds the specified value with the specified name to the LRU. If the LRU + * is full, an eviction is performed inline. + *

+ * A RuntimeException is thrown if the entry already exists in the cache. + * @param name + * @param value + */ + public void add(String name, V value) { + if (map.containsKey(name)) { + throw new RuntimeException("Added existing entry"); + } + if (size >= capacity) { + evict(); + } + Entry entry = null; + if (head == null) { + head = new Entry(name, value, null, null); + entry = head; + } else if (tail == null) { + tail = new Entry(name, value, head, null); + head.next = tail; + entry = tail; + } else { + entry = new Entry(name, value, tail, null); + tail.next = entry; + tail = entry; + } + map.put(name, entry); + size++; + } + + /** + * Get the value for the specified name, if it exists. Returns null if the + * entry does not exist. + * @param name + * @return value for the specified name, or null if not found + */ + public V get(String name) { + Entry entry = map.get(name); + if (entry == null) return null; + if (tail == null || entry.next == null) return entry.value; + if (entry.prev == null) { + head = entry.next; + head.prev = null; + } else { + entry.prev.next = entry.next; + entry.next.prev = entry.prev; + } + tail.next = entry; + entry.prev = tail; + entry.next = null; + tail = entry; + return entry.value; + } + + private void evict() { + Entry entry = head; + for (int i=0;i entry = head; + System.out.print("HEAD (" + ((head != null) ? head.name : "null") + ") -> "); + while (entry != null) { + System.out.print(entry.name + " -> "); + entry = entry.next; + } + System.out.println(" (" + ((tail != null) ? tail.name : "null") + ") TAIL"); + System.out.println("MapSize=" + map.size() + ", Size=" + size); + Entry shouldBeTail = head; + if (head != null && tail != null) { + while (true) { + if (shouldBeTail.next == null) break; + shouldBeTail = shouldBeTail.next; + } + if (!shouldBeTail.name.equals(tail.name)) { + System.out.println("MISMATCH!"); + System.exit(1); + } + } + } + + /** Entry in the LRU */ + static class Entry { + + /** Unique name of the entry in the LRU */ + String name; + + /** Value of LRU entry */ + V value; + + /** Previous pointer in LRU */ + Entry prev; + + /** Next pointer in LRU */ + Entry next; + + /** Construct a new LRU entry */ + Entry(String name, V value, Entry prev, Entry next) { + this.name = name; + this.value = value; + this.prev = prev; + this.next = next; + } + } +} Index: src/main/java/org/apache/hadoop/hbase/bench/BlockCacheBench.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/bench/BlockCacheBench.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/bench/BlockCacheBench.java (revision 0) @@ -0,0 +1,431 @@ +package org.apache.hadoop.hbase.bench; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.io.hfile.LruBlockCache; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.util.GenericOptionsParser; + +/** + * Isolated benchmark of the HBase LRU Block Cache. + * + * Primarily to be used for testing of GC performance of the LRU with various + * heap sizes and memory management techniques. + * + * @author jgray + */ +public class BlockCacheBench { + + // Internal class members + + /** Configuration used for block cache */ + private final Configuration conf; + + /** Maximum size of LRU */ + private final long maxSize; + + /** Size of each block */ + private final int blockSize; + + /** Number of total blocks that exist */ + private final long numBlocks; + + /** Standard deviation for request distribution */ + private final long stdev; + + /** Number of iterations to run */ + private final long iterations; + + /** Logging interval */ + private final int logInterval; + + /** Sleep interval */ + private final int sleepInterval; + + /** Sleep length */ + private final int sleepLength; + + /** Random generated data */ + private final byte [] randomData; + + /** LRU Block Cache */ + private LruBlockCache lru; + + /** Block cache worker threads */ + private BlockCacheWorker [] workers; + + /** Block cache writer threads */ + private BlockCacheWriter [] writers; + + /** Block cache reader threads */ + private BlockCacheReader [] readers; + + /** Pause monitoring thread */ + private PauseMonitor monitor; + + /** Global count of operations completed */ + private volatile long count = 0; + + /** Global count of hits */ + private volatile long hits = 0; + + /** Global count of misses */ + private volatile long misses = 0; + + // Configuration parameters and defaults + + private static final String blockName = "DataBlockName"; + + private static final String LOG_INTERVAL_CONF = "lru.bench.log.interval"; + private static final int LOG_INTERVAL_DEFAULT = 10000; + + private static final String SLEEP_INTERVAL_CONF = "lru.bench.sleep.interval"; + private static final int SLEEP_INTERVAL_DEFAULT = 100; + + private static final String SLEEP_LENGTH_CONF = "lru.bench.sleep.length"; + private static final int SLEEP_LENGTH_DEFAULT = 50; + + private static final String WORKER_THREADS_CONF = "lru.bench.worker.threads"; + private static final int WORKER_THREADS_DEFAULT = 2; + +// private static final String WRITER_THREADS_CONF = "lru.bench.writer.threads"; +// private static final int WRITER_THREADS_DEFAULT = 2; +// +// private static final String READER_THREADS_CONF = "lru.bench.reader.threads"; +// private static final int READER_THREADS_DEFAULT = 2; + + private static final String THREAD_DELAY_CONF = "lru.bench.thread.delay"; + private static final long THREAD_DELAY_DEFAULT = 1000; + + private static final String MONITOR_SLEEP_CONF = "lru.bench.monitor.sleep"; + private static final long MONITOR_SLEEP_DEFAULT = 1000; + + private static final String MONITOR_LIMIT_CONF = "lru.bench.monitor.limit"; + private static final long MONITOR_LIMIT_DEFAULT = 1050; + + public BlockCacheBench(Configuration conf, long maxSize, int blockSize, + long numBlocks, int stdev, long iterations) { + this.conf = conf; + this.maxSize = maxSize; + this.blockSize = blockSize; + this.numBlocks = numBlocks; + this.stdev = stdev; + this.iterations = iterations; + this.logInterval = conf.getInt(LOG_INTERVAL_CONF, LOG_INTERVAL_DEFAULT); + this.sleepInterval = conf.getInt(SLEEP_INTERVAL_CONF, + SLEEP_INTERVAL_DEFAULT); + this.sleepLength = conf.getInt(SLEEP_LENGTH_CONF, SLEEP_LENGTH_DEFAULT); + + // Generate random data + this.randomData = RandomGenerator.makeRandomBytes(blockSize); + } + + public void initialize() throws IOException { + // Initialize block cache + log("Initializing LRU Block Cache [maxsize=" + maxSize + "] [blockSize=" + + blockSize + "]"); + this.lru = new LruBlockCache(maxSize, blockSize, conf); + log("LRU initialized: " + lru.generateStatString()); + } + + public void startThreads() throws IOException, InterruptedException { + + // Start pause monitoring thread + this.monitor = new PauseMonitor( + conf.getLong(MONITOR_SLEEP_CONF, MONITOR_SLEEP_DEFAULT), + conf.getLong(MONITOR_LIMIT_CONF, MONITOR_LIMIT_DEFAULT)); + monitor.setDaemon(true); + monitor.start(); + + long threadLaunchDelay = conf.getLong(THREAD_DELAY_CONF, + THREAD_DELAY_DEFAULT); + + // Start worker threads + int numWorkers = conf.getInt(WORKER_THREADS_CONF, WORKER_THREADS_DEFAULT); + if (numWorkers != 0) { + log("Starting " + numWorkers + " worker threads"); + this.workers = new BlockCacheWorker[numWorkers]; + for (int i=0; i limit) { + log("PauseMonitor slept " + diff + " milliseconds; sleep=" + sleep + + ", limit=" + limit + ", priorSuccessCount=" + count); + count = 0; + } else { + count++; + if (count % 5 == 0) { + log("PauseMonitor slept normally 5 times in a row"); + } + } + } + } + } + + // Private helpers + + private static void log(String msg) { + System.out.println(msg); + } + + private static long parseLong(String str) { + try { + return Long.parseLong(str); + } catch (NumberFormatException nfe) { + usage("Expected a numeric long value but got '" + str + "'"); + System.exit(-1); + return -1; + } + } + + private static int parseInteger(String str) { + try { + return Integer.parseInt(str); + } catch (NumberFormatException nfe) { + usage("Expected a numeric integer value but got '" + str + "'"); + System.exit(-1); + return -1; + } + } + + private static void usage(String error) { + log("Invalid parameters: " + error); + log("Usage: BlockCacheBench [-D ]* " + + " "); + } + + // Main + + public static void main(String [] args) throws Exception { + Configuration conf = HBaseConfiguration.create(); + String [] remArgs = new GenericOptionsParser(conf, args).getRemainingArgs(); + + if (remArgs.length != 5) { + usage("Only gave " + args.length + " parameters (5 required)"); + System.exit(-1); + } + + long maxSize = parseLong(remArgs[0]); + int blockSize = parseInteger(remArgs[1]); + long numBlocks = parseLong(remArgs[2]); + int stdev = parseInteger(remArgs[3]); + long iterations = parseLong(remArgs[4]); + + BlockCacheBench bench = new BlockCacheBench(conf, maxSize, blockSize, + numBlocks, stdev, iterations); + bench.initialize(); + bench.startThreads(); + bench.waitForCompletion(); + } +} Index: src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java (revision 20242) +++ src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java (working copy) @@ -624,10 +624,13 @@ public void logStats() { if (!LOG.isDebugEnabled()) return; - // Log size + LruBlockCache.LOG.debug(generateStatString()); + } + + public String generateStatString() { long totalSize = heapSize(); long freeSize = maxSize - totalSize; - LruBlockCache.LOG.debug("LRU Stats: " + + return "LRU Stats: " + "total=" + StringUtils.byteDesc(totalSize) + ", " + "free=" + StringUtils.byteDesc(freeSize) + ", " + "max=" + StringUtils.byteDesc(this.maxSize) + ", " + @@ -642,7 +645,7 @@ (stats.getHitCachingCount() == 0 ? "0" : (StringUtils.formatPercent(stats.getHitCachingRatio(), 2)+ ", ")) + "evictions=" + stats.getEvictionCount() + ", " + "evicted=" + stats.getEvictedCount() + ", " + - "evictedPerRun=" + stats.evictedPerEviction()); + "evictedPerRun=" + stats.evictedPerEviction(); } /** Index: FACEBOOK.txt =================================================================== --- FACEBOOK.txt (revision 20242) +++ FACEBOOK.txt (working copy) @@ -396,3 +396,5 @@ 11/04/2011: Pull of r1196674 (HBASE-4716) from public trunk (HBASE-4716 Improve locking for single column family bulk load) + +11/14/2011: Memory/GC benchmarks and tools (jgray)