s/BenchmarkRunner.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Tue, 30 Sep 2014 10:06:32 +0100
changeset 223 72287b3d9937
parent 205 63a0130d7951
child 224 703461d83b2f
permissions -rw-r--r--
Removed --script paramter (alias for --arguments) Recent version of Smalltalk/X handles --script itself si it is not passed to the client script (sigh)

"{ Package: 'jv:calipel/s' }"

Object subclass:#BenchmarkRunner
	instanceVariableNames:'suite result'
	classVariableNames:''
	poolDictionaries:''
	category:'CalipeL-S-Core-Runner'
!

!BenchmarkRunner class methodsFor:'documentation'!

documentation
"
    BechmarkRunner is the command line client for CalipeL.
    It parses the command line arguments, sets up the
    environment and run benchmarks. 
"
! !

!BenchmarkRunner class methodsFor:'instance creation'!

new
    "return an initialized instance"

    ^ self basicNew initialize.
! !

!BenchmarkRunner methodsFor:'accessing'!

result
    ^ result
!

suite
    ^ suite
! !

!BenchmarkRunner methodsFor:'main'!

main:argv0
    | i report name file params classes runs argv desc setup setupScript teardown teardownScript |

    params := Dictionary new.
    classes := OrderedCollection new.
    report := BenchmarkReport text.
    runs := 5.
    argv := argv0 asOrderedCollection.

    i := 1.
    [ i <= argv size ] whileTrue:[
        | arg |

        arg := argv at: i.
        i := i + 1.
        arg first == $- ifTrue:[
            arg = '-n' ifTrue:[
                i > argv size ifTrue:[
                    self error:'-n requires an integer parameter'
                ].
                runs := Integer readFrom: (argv at: i) onError:[
                    self error: '-n requires an integer parameter'.
                ].
                i := i + 1.
            ].
            arg = '-o' ifTrue:[
                i > argv size ifTrue:[
                    self error:'-o requires a filename parameter'
                ].
                file := argv at: i.
                i := i + 1.
            ].
            (arg = '--arguments' or:[arg = '--script']) ifTrue:[
                | arguments argumentsF |

                i > argv size ifTrue:[
                    self error: arg, ' requires a file parameter.'
                ].                
                argumentsF := (arguments := (argv at: i)) asFilename.
                argumentsF exists ifFalse:[
                    self error:'no such file: ', arguments.
                ].
                argumentsF readingFileDo:[:f | self parseArguments: f into: argv ].
                i := i + 1.
            ].  
                         

            arg = '-r' ifTrue:[
                | reportNm |

                i > argv size ifTrue:[
                    self error:'-r requires a valid class name parameter'
                ].
                reportNm := (argv at: i).
                i := i + 1.
                report := Smalltalk at: reportNm asSymbol.
                report isNil ifTrue:[
                    self error: 'No report class named ''', reportNm, ''''.
                ].
            ].
            arg = '--text' ifTrue:[
                report := BenchmarkReport text.
            ].

            arg = '--json' ifTrue:[
                report := BenchmarkReport json.
            ].

            arg = '--tag' ifTrue:[
                i > argv size ifTrue:[
                    self error:'--tag requires a string parameter.'
                ].                
                name := argv at: i.
                i := i + 1.
            ].

            arg = '--setup' ifTrue:[ 
                i > argv size ifTrue:[
                    self error:'--setup requires a string parameter.'
                ].                
                setup := argv at: i.
                i := i + 1.     
            ].
            arg = '--teardown' ifTrue:[ 
                i > argv size ifTrue:[
                    self error:'--teardown requires a string parameter.'
                ].                
                teardown := argv at: i.
                i := i + 1.     
            ].

            (arg = '--setup-script') ifTrue:[
                i > argv size ifTrue:[
                    self error: arg, ' requires a file parameter.'
                ].                
                setupScript := (argv at: i) asFilename.
                setupScript exists ifFalse:[
                    self error:'no such file: ', (argv at: i).
                ].
                i := i + 1.
            ].  
            (arg = '--teardown-script') ifTrue:[
                i > argv size ifTrue:[
                    self error: arg, ' requires a file parameter.'
                ].                
                teardownScript := (argv at: i) asFilename.
                teardownScript exists ifFalse:[
                    self error:'no such file: ', (argv at: i).
                ].
                i := i + 1.
            ].  



            arg = '--description' ifTrue:[
                i > argv size ifTrue:[
                    self error:'--description requires a string parameter.'
                ].                
                desc := argv at: i.
                i := i + 1.
            ].    

            arg second = $D ifTrue:[
                | eqIdx |

                ((arg size > 2) and:[(eqIdx := arg indexOf: $= startingAt: 3) ~~ 0]) ifTrue:[
                    params at: (arg copyFrom: 3 to: eqIdx - 1) put: (arg copyFrom: eqIdx + 1)
                ] ifFalse:[
                    self error: 'No parameter value'
                ]
            ]
        ] ifFalse:[
            classes add: arg.
        ]
    ].


    classes isEmpty ifTrue:[
        self error:'No suite or benchmark specified.'
    ].

    "Build suite"
    suite := BenchmarkSuite new.
    classes do:[:each|
        | classNm class selector |    
        i := each indexOf: $#.
        i ~~ 0 ifTrue:[
            classNm := each copyTo: i - 1.
            selector := (each copyFrom: i + 1) asSymbol.
        ] ifFalse:[
            classNm := each.
        ].
        class := Smalltalk at: classNm asSymbol.
        class isNil ifTrue:[
            self error: 'Class ', classNm , ' does not exists'.
        ].
        Smalltalk isSmalltalkX ifTrue:[
            class isLoaded ifFalse:[class autoload].
        ].
        selector isNil ifTrue:[
            suite addBenchmark: (BenchmarkSuite class: class)
        ] ifFalse:[
            suite addBenchmark: (BenchmarkSuite class:class selector:selector)
        ]
    ].

    "Run setup, if any"
    setup notNil ifTrue:[ Compiler evaluate: setup ].
    setupScript notNil ifTrue:[ Compiler evaluate: setupScript asFilename contents asString ].

    "Run suite"
    result := BenchmarkResult new.
    result runs: runs.
    suite run: result with: params executor: BenchmarkRunnerExecutor new.

    "Write report"
    file notNil ifTrue:[
        file asFilename writingFileDo:[:s|
            report
                name: name;
                description: desc;      
                write: result on: s 
        ]
    ] ifFalse:[
        report
            name: name;
            description: desc;
            write: result on: BenchmarkPlatform current stdout
    ].

    "Run teardown"
    teardown notNil ifTrue:[ Compiler evaluate: teardown ].
    teardownScript notNil ifTrue:[ Compiler evaluate: teardownScript asFilename contents asString ].

    "Modified: / 08-03-2014 / 16:23:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

options
    "Prints common command line options to stderr"

    BenchmarkPlatform current stderr nextPutAll:
'
Common options:
  -o FILE ................ write output to FILE instead of to standard output (default: stdout)
  -n RUNS ................ how many times to run each bechmark (default: 5)
  -r REPORTCLASS ......... user REPORTCLASS to generate report (default: BenchmarkReportText)
  --arguments FILE ....... read additional arguments from FILE
  --setup EXPR ........... evaluate EXPR before actually running any benchmark
  --setup-script FILE .... evaluate code in FILE before actually running any benchmark
  --teardown EXPR ........ evaluate EXPR after all benchmarks are run
  --teardown-script FILE . evaluate code in FILE  after all benchmarks are run
  --text ................. generate text report (equivalent to -r BenchmarkReportText)
  --json ................. generate JSON report (equivalent to -r BenchmarkReportJSON)
  --tag TAG .............. tag for the current benchmark set (default: "default")
  --description .......... short string describing the current configuration (default: nothing)
  -DNAME=VALUE ........... defines a parameter NAME with value of VALUE

Suitespec format:
  <suitespec> is CLASSNAME[#SELECTOR]

More documetation:
  see https://bitbucket.org/janvrany/jv-calipel/wiki/Home

'

    "Created: / 06-06-2013 / 11:01:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 30-09-2014 / 09:57:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!BenchmarkRunner methodsFor:'private-parsing'!

parseArgument:stream 
    | c |

    [
        c := stream peek.
        c == $#
    ] whileTrue:[
        self parseComment:stream.
        stream skipSeparators.
        stream atEnd ifTrue:[ ^ nil ].
    ].
    c == $\ ifTrue:[
        stream next.
        [
            stream peek == Character space or:[ stream peek == Character tab ]
        ] whileTrue:[ stream next. ].
        stream peek == Character cr ifTrue:[
            stream skipSeparators.
            ^ self parseArgument:stream.
        ] ifFalse:[ ^ '\' ].
    ].
    c == $" ifTrue:[
        ^ self parseQuoted1:stream
    ].
    c == $' ifTrue:[
        ^ self parseQuoted2:stream
    ].
    ^ self parseToken:stream

    "Created: / 08-03-2014 / 11:01:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-03-2014 / 15:59:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parseArguments: stream into: argv
    [ stream skipSeparators. stream atEnd ] whileFalse:[
        | arg |

        arg := (self parseArgument: stream).
        arg notNil ifTrue:[ argv add: arg ]
    ].

    "Created: / 08-03-2014 / 10:59:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-03-2014 / 15:59:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parseComment: stream 
    stream nextLine.

    "Created: / 08-03-2014 / 11:18:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parseQuoted1:stream
    "Parse string quoted by $" 
    stream next. "/ eat $"
    ^ String streamContents:[:s|
        [ stream peek == $"] whileFalse:[ 
            stream peek == $\ ifTrue:[ 
                stream next.
            ].
            stream atEnd ifTrue:[ 
                self error:'Unterminated string'.
            ].
            s nextPut: stream next.
        ].
        stream next "/ eat $"
    ].

    "Created: / 08-03-2014 / 11:18:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-03-2014 / 15:44:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parseQuoted2:stream
    "Parse string quoted by $'" 
    stream next. "/ eat $'
    ^ String streamContents:[:s|
        [ stream peek == $'] whileFalse:[ 
            stream atEnd ifTrue:[ 
                self error:'Unterminated string'.
            ].
            s nextPut: stream next.
        ].
        stream next. "/ eat $'       
    ].

    "Created: / 08-03-2014 / 11:18:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-03-2014 / 15:44:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parseToken: stream
    ^ String streamContents:[:s|
        [ stream atEnd or:[stream peek isSeparator] ] whileFalse:[ 
            s nextPut: stream next.
        ].
    ].

    "Created: / 08-03-2014 / 11:18:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-03-2014 / 15:36:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!BenchmarkRunner class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
! !