From 7b64aea46e537b8764125fa52f85578612467d0a Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Thu, 8 Jul 2021 14:54:19 +0100 Subject: [PATCH 1/7] start on benchmarking (parsers) --- bench/Bench.hs | 70 +++++++++++++++++++++++++++++++++++++++++++++++ fortran-src.cabal | 28 +++++++++++++++++++ package.yaml | 8 ++++++ 3 files changed, 106 insertions(+) create mode 100644 bench/Bench.hs diff --git a/bench/Bench.hs b/bench/Bench.hs new file mode 100644 index 00000000..1ab9da84 --- /dev/null +++ b/bench/Bench.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE OverloadedStrings #-} + +import Criterion.Main +import Language.Fortran.Parser.Any +import Language.Fortran.ParserMonad +import Language.Fortran.Version +import Language.Fortran.AST + +import qualified Language.Fortran.Parser.Fortran90 as F90 +import qualified Language.Fortran.Lexer.FreeForm as LexFree + +import Data.ByteString (ByteString) +import qualified Data.Text as Text +import qualified Data.Text.Encoding as TSE +import Data.List (intercalate) + +main :: IO () +main = defaultMain + [ bgroup "parser" + [ bgroup "Fortran 90" + [ bench "statement (lone)" $ whnf pF90Stmt snippetFreeStmt + , bench "function (lone)" $ whnf pF90Func snippetFreeFuncComplex + ] + ] + ] + +pF90Stmt :: ByteString -> Statement A0 +pF90Stmt = parseFree Fortran90 F90.statementParser + +pF90Func :: ByteString -> ProgramUnit A0 +pF90Func = parseFree Fortran90 F90.functionParser + +parseFree :: FortranVersion + -> Parse LexFree.AlexInput LexFree.Token a -> ByteString -> a +parseFree ver parser src = evalParse parser parserState + where parserState = LexFree.initParseState src ver "" + +-------------------------------------------------------------------------------- + +snippetFreeStmt :: ByteString +snippetFreeStmt = programListing $ + [ "character(5) :: assign_in_decl*5 = \"test!\"" + ] + +snippetFreeFuncComplex :: ByteString +snippetFreeFuncComplex = programListing $ + [ "integer function f(x, y, z) result(i)" + , " print *, i" + , " i = (i - 1)" + , "end function f" + ] + +-------------------------------------------------------------------------------- + +-- | unlines but without the trailing newline +programListing :: [String] -> ByteString +programListing = strToByteString . intercalate "\n" + +strToByteString :: String -> ByteString +strToByteString = TSE.encodeUtf8 . Text.pack + +-------------------------------------------------------------------------------- + +f90ProgramFile :: ByteString +f90ProgramFile = strToByteString $ intercalate "\n" $ + [ "program main" + , " character(5) :: assign_in_decl*5 = \"test!\"" + , " assign_out_decl = \"test!\"" + , "end program main" + ] diff --git a/fortran-src.cabal b/fortran-src.cabal index 234d9058..575c3118 100644 --- a/fortran-src.cabal +++ b/fortran-src.cabal @@ -269,3 +269,31 @@ test-suite spec , text >=1.2 && <2 , uniplate >=1.6 && <2 default-language: Haskell2010 + +benchmark bench + type: exitcode-stdio-1.0 + main-is: Bench.hs + other-modules: + Paths_fortran_src + hs-source-dirs: + bench + ghc-options: -Wall + build-depends: + GenericPretty >=1.2.2 && <2 + , array ==0.5.* + , base >=4.6 && <5 + , binary >=0.8.3.0 && <0.11 + , bytestring >=0.10 && <0.12 + , containers >=0.5 && <0.7 + , criterion ==1.5.* + , deepseq ==1.4.* + , directory >=1.2 && <2 + , fgl ==5.* + , filepath ==1.4.* + , fortran-src + , mtl >=2.2 && <3 + , pretty >=1.1 && <2 + , temporary >=1.2 && <1.4 + , text >=1.2 && <2 + , uniplate >=1.6 && <2 + default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 813a9866..983d975e 100644 --- a/package.yaml +++ b/package.yaml @@ -96,3 +96,11 @@ tests: - deepseq >=1.4 && <1.5 - hspec >=2.2 && <3 - QuickCheck >=2.10 && <2.15 + +benchmarks: + bench: + main: Bench.hs + source-dirs: bench + dependencies: + - fortran-src + - criterion >= 1.5 && <1.6 From 67ca38bdbcbb7af21409a9a3dd1e88be2ed573f9 Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Thu, 8 Jul 2021 15:21:17 +0100 Subject: [PATCH 2/7] add CI job for running benchmarks --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17f3801d..16a50cd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,7 @@ jobs: - name: Build and run tests run: stack --no-terminal haddock --test --no-haddock-deps --pedantic +<<<<<<< HEAD - name: Install (to place executable at a known location) run: stack --no-terminal install @@ -51,6 +52,11 @@ jobs: runs-on: ubuntu-latest name: Ubuntu / GHC ${{ matrix.ghc }}, Cabal / test +======= + ubuntu-cabal-test: + runs-on: ubuntu-latest + name: Ubuntu / GHC ${{ matrix.ghc }}, Cabal ${{ matrix.cabal }} / test +>>>>>>> b360279 (add CI job for running benchmarks) strategy: fail-fast: false # don't stop if one job (= GHC version) fails matrix: @@ -198,3 +204,34 @@ jobs: path: "C:/cabal/bin/${{ env.EXE_NAME }}.exe" name: ${{ env.EXE_NAME }}-windows-ghc-${{ matrix.ghc }}-cabal-${{ github.sha }}.exe if-no-files-found: error + + ubuntu-stack-bench: + runs-on: ubuntu-latest + name: Ubuntu / Stack / benchmark + steps: + - uses: actions/checkout@v2 + + # relative paths are relative to the project directory + - name: Cache Stack build artifacts (user + project) + uses: actions/cache@v2 + with: + path: | + ~/.stack + .stack-work + # best effort for cache: tie it to Stack resolver and package config + key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml.lock', 'package.yaml') }} + restore-keys: | + ${{ runner.os }}-stack + + - name: Build benchmarks + run: stack build --bench --no-run-benchmarks + + # the weird piping is for copying STDERR + - name: Run benchmarks + run: stack --no-terminal bench 2> >(tee bench.log >&2) + + - name: Archive benchmark log + uses: actions/upload-artifact@v2 + with: + name: benchmark-log-ubuntu-stack.txt + path: bench.log From 946d00c5a663759954cc85756653b94d2680a56a Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Mon, 12 Jul 2021 09:09:40 +0100 Subject: [PATCH 3/7] add F66 benchs --- bench/Bench.hs | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index 1ab9da84..70fbf27e 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -7,7 +7,9 @@ import Language.Fortran.Version import Language.Fortran.AST import qualified Language.Fortran.Parser.Fortran90 as F90 +import qualified Language.Fortran.Parser.Fortran66 as F66 import qualified Language.Fortran.Lexer.FreeForm as LexFree +import qualified Language.Fortran.Lexer.FixedForm as LexFixed import Data.ByteString (ByteString) import qualified Data.Text as Text @@ -18,32 +20,52 @@ main :: IO () main = defaultMain [ bgroup "parser" [ bgroup "Fortran 90" - [ bench "statement (lone)" $ whnf pF90Stmt snippetFreeStmt - , bench "function (lone)" $ whnf pF90Func snippetFreeFuncComplex + [ bench "statement (expr)" $ whnf pF90Stmt snippetFreeStmtExpr + , bench "statement (assign)" $ whnf pF90Stmt snippetFreeStmtDeclAssign + , bench "function" $ whnf pF90Func snippetFreeFunc + ] + , bgroup "Fortran 66" + [ bench "statement (expr)" $ whnf pF66Stmt snippetFixedStmt + , bench "function" $ whnf pF90Func snippetFixedFunc ] ] ] +-------------------------------------------------------------------------------- + pF90Stmt :: ByteString -> Statement A0 pF90Stmt = parseFree Fortran90 F90.statementParser pF90Func :: ByteString -> ProgramUnit A0 pF90Func = parseFree Fortran90 F90.functionParser +pF66Stmt :: ByteString -> Statement A0 +pF66Stmt = parseFixed Fortran66 F66.statementParser + parseFree :: FortranVersion -> Parse LexFree.AlexInput LexFree.Token a -> ByteString -> a parseFree ver parser src = evalParse parser parserState where parserState = LexFree.initParseState src ver "" +parseFixed :: FortranVersion + -> Parse LexFixed.AlexInput LexFixed.Token a -> ByteString -> a +parseFixed ver parser src = evalParse parser parserState + where parserState = LexFixed.initParseState src ver "" + -------------------------------------------------------------------------------- -snippetFreeStmt :: ByteString -snippetFreeStmt = programListing $ +snippetFreeStmtExpr :: ByteString +snippetFreeStmtExpr = programListing $ + [ "x = y*2 + z - 1" + ] + +snippetFreeStmtDeclAssign :: ByteString +snippetFreeStmtDeclAssign = programListing $ [ "character(5) :: assign_in_decl*5 = \"test!\"" ] -snippetFreeFuncComplex :: ByteString -snippetFreeFuncComplex = programListing $ +snippetFreeFunc :: ByteString +snippetFreeFunc = programListing $ [ "integer function f(x, y, z) result(i)" , " print *, i" , " i = (i - 1)" @@ -52,6 +74,21 @@ snippetFreeFuncComplex = programListing $ -------------------------------------------------------------------------------- +snippetFixedStmt :: ByteString +snippetFixedStmt = programListing $ + [ " x = y*2 + z - 1" + ] + +snippetFixedFunc :: ByteString +snippetFixedFunc = programListing $ + [ " subroutine f(x, y, z)" + , " print *, i" + , " i = (i - 1)" + , " end" + ] + +-------------------------------------------------------------------------------- + -- | unlines but without the trailing newline programListing :: [String] -> ByteString programListing = strToByteString . intercalate "\n" From f427773ef17b9f16be9e997e096a6e56e339599f Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Mon, 12 Jul 2021 10:34:40 +0100 Subject: [PATCH 4/7] add F77 benches; bench post-parse transformations --- bench/Bench.hs | 83 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index 70fbf27e..082b3048 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -7,6 +7,7 @@ import Language.Fortran.Version import Language.Fortran.AST import qualified Language.Fortran.Parser.Fortran90 as F90 +import qualified Language.Fortran.Parser.Fortran77 as F77 import qualified Language.Fortran.Parser.Fortran66 as F66 import qualified Language.Fortran.Lexer.FreeForm as LexFree import qualified Language.Fortran.Lexer.FixedForm as LexFixed @@ -23,10 +24,20 @@ main = defaultMain [ bench "statement (expr)" $ whnf pF90Stmt snippetFreeStmtExpr , bench "statement (assign)" $ whnf pF90Stmt snippetFreeStmtDeclAssign , bench "function" $ whnf pF90Func snippetFreeFunc + , bench "ProgramFile (default transformations)" $ whnf pF90pfDefTs snippetFreePF + , bench "ProgramFile (no transformations)" $ whnf pF90pfNoTs snippetFreePF + ] + , bgroup "Fortran 77" + [ bench "statement (expr)" $ whnf pF77Stmt snippetFixedStmt + --, bench "function" $ whnf pF77Func snippetFixedFunc + , bench "ProgramFile (default transformations)" $ whnf pF77pfDefTs snippetFixedPF + , bench "ProgramFile (no transformations)" $ whnf pF77pfNoTs snippetFixedPF ] , bgroup "Fortran 66" [ bench "statement (expr)" $ whnf pF66Stmt snippetFixedStmt - , bench "function" $ whnf pF90Func snippetFixedFunc + -- , bench "function" $ whnf pF66Func snippetFixedFunc + --, bench "ProgramFile (default transformations)" $ whnf pF66pfDefTs snippetFixedPF + --, bench "ProgramFile (no transformations)" $ whnf pF66pfNoTs snippetFixedPF ] ] ] @@ -39,9 +50,45 @@ pF90Stmt = parseFree Fortran90 F90.statementParser pF90Func :: ByteString -> ProgramUnit A0 pF90Func = parseFree Fortran90 F90.functionParser +pF90pfDefTs :: ByteString -> ProgramFile A0 +pF90pfDefTs = parseFull F90.fortran90Parser + +pF90pfNoTs :: ByteString -> ProgramFile A0 +pF90pfNoTs = parseFull (F90.fortran90ParserWithTransforms []) + +pF77Stmt :: ByteString -> Statement A0 +pF77Stmt = parseFixed Fortran77 F77.statementParser + +--pF77Func :: ByteString -> ProgramUnit A0 +--pF77Func = parseFixed Fortran77 F77.functionParser + +pF77pfDefTs :: ByteString -> ProgramFile A0 +pF77pfDefTs = parseFull F77.fortran77Parser + +pF77pfNoTs :: ByteString -> ProgramFile A0 +pF77pfNoTs = parseFull (F77.fortran77ParserWithTransforms []) + pF66Stmt :: ByteString -> Statement A0 pF66Stmt = parseFixed Fortran66 F66.statementParser +--pF66Func :: ByteString -> Statement A0 +--pF66Func = parseFixed Fortran66 F66.functionParser + +pF66pfDefTs :: ByteString -> ProgramFile A0 +pF66pfDefTs = parseFull F66.fortran66Parser + +pF66pfNoTs :: ByteString -> ProgramFile A0 +pF66pfNoTs = parseFull (F66.fortran66ParserWithTransforms []) + +-- polymorphic on lexer input+token in order to work with fixed & free form +parseFull + :: (ByteString -> String -> ParseResult alexin token (ProgramFile A0)) + -> ByteString -> ProgramFile A0 +parseFull parser bs = + case parser bs "" of + ParseOk a _ -> a + ParseFailed _ -> error "error" + parseFree :: FortranVersion -> Parse LexFree.AlexInput LexFree.Token a -> ByteString -> a parseFree ver parser src = evalParse parser parserState @@ -72,6 +119,18 @@ snippetFreeFunc = programListing $ , "end function f" ] +snippetFreePF :: ByteString +snippetFreePF = programListing $ + [ "integer function f(x, y, z) result(i)" + , " print *, i" + , " i = (i - 1)" + , "end function f" + , "" + , "program main" + , " x = 1 + 2" + , "end program main" + ] + -------------------------------------------------------------------------------- snippetFixedStmt :: ByteString @@ -87,6 +146,18 @@ snippetFixedFunc = programListing $ , " end" ] +snippetFixedPF :: ByteString +snippetFixedPF = programListing $ + [ " subroutine f(x, y, z)" + , " print *, i" + , " i = (i - 1)" + , " end" + , "" + , " program main" + , " x = 1 + 2" + , " end" + ] + -------------------------------------------------------------------------------- -- | unlines but without the trailing newline @@ -95,13 +166,3 @@ programListing = strToByteString . intercalate "\n" strToByteString :: String -> ByteString strToByteString = TSE.encodeUtf8 . Text.pack - --------------------------------------------------------------------------------- - -f90ProgramFile :: ByteString -f90ProgramFile = strToByteString $ intercalate "\n" $ - [ "program main" - , " character(5) :: assign_in_decl*5 = \"test!\"" - , " assign_out_decl = \"test!\"" - , "end program main" - ] From d5c8ba233d0612d31b25cb70f4cedee1400f8254 Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Mon, 17 Jan 2022 09:57:09 +0000 Subject: [PATCH 5/7] fix CI (bad rebase) --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a50cd8..17866980 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,6 @@ jobs: - name: Build and run tests run: stack --no-terminal haddock --test --no-haddock-deps --pedantic -<<<<<<< HEAD - name: Install (to place executable at a known location) run: stack --no-terminal install @@ -52,11 +51,6 @@ jobs: runs-on: ubuntu-latest name: Ubuntu / GHC ${{ matrix.ghc }}, Cabal / test -======= - ubuntu-cabal-test: - runs-on: ubuntu-latest - name: Ubuntu / GHC ${{ matrix.ghc }}, Cabal ${{ matrix.cabal }} / test ->>>>>>> b360279 (add CI job for running benchmarks) strategy: fail-fast: false # don't stop if one job (= GHC version) fails matrix: From 82210c3825db4309d391dfdb4e3ce27aabc8ba96 Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Tue, 1 Feb 2022 12:51:52 +0000 Subject: [PATCH 6/7] update --- bench/Bench.hs | 78 +++++++++-------------------------------------- bench/issue-190.f | 4 +++ fortran-src.cabal | 20 ++++++++++++ 3 files changed, 39 insertions(+), 63 deletions(-) create mode 100644 bench/issue-190.f diff --git a/bench/Bench.hs b/bench/Bench.hs index 082b3048..dbd8aabe 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -1,22 +1,18 @@ {-# LANGUAGE OverloadedStrings #-} import Criterion.Main -import Language.Fortran.Parser.Any -import Language.Fortran.ParserMonad + +import qualified Language.Fortran.Parser as Parser import Language.Fortran.Version import Language.Fortran.AST -import qualified Language.Fortran.Parser.Fortran90 as F90 -import qualified Language.Fortran.Parser.Fortran77 as F77 -import qualified Language.Fortran.Parser.Fortran66 as F66 -import qualified Language.Fortran.Lexer.FreeForm as LexFree -import qualified Language.Fortran.Lexer.FixedForm as LexFixed - import Data.ByteString (ByteString) -import qualified Data.Text as Text -import qualified Data.Text.Encoding as TSE +import qualified Data.Text as Text +import qualified Data.Text.Encoding as Text import Data.List (intercalate) +import qualified Language.Fortran.Parser.Free.Fortran90 as F90 + main :: IO () main = defaultMain [ bgroup "parser" @@ -45,59 +41,15 @@ main = defaultMain -------------------------------------------------------------------------------- pF90Stmt :: ByteString -> Statement A0 -pF90Stmt = parseFree Fortran90 F90.statementParser - -pF90Func :: ByteString -> ProgramUnit A0 -pF90Func = parseFree Fortran90 F90.functionParser - -pF90pfDefTs :: ByteString -> ProgramFile A0 -pF90pfDefTs = parseFull F90.fortran90Parser - -pF90pfNoTs :: ByteString -> ProgramFile A0 -pF90pfNoTs = parseFull (F90.fortran90ParserWithTransforms []) - -pF77Stmt :: ByteString -> Statement A0 -pF77Stmt = parseFixed Fortran77 F77.statementParser - ---pF77Func :: ByteString -> ProgramUnit A0 ---pF77Func = parseFixed Fortran77 F77.functionParser - -pF77pfDefTs :: ByteString -> ProgramFile A0 -pF77pfDefTs = parseFull F77.fortran77Parser - -pF77pfNoTs :: ByteString -> ProgramFile A0 -pF77pfNoTs = parseFull (F77.fortran77ParserWithTransforms []) - -pF66Stmt :: ByteString -> Statement A0 -pF66Stmt = parseFixed Fortran66 F66.statementParser - ---pF66Func :: ByteString -> Statement A0 ---pF66Func = parseFixed Fortran66 F66.functionParser - -pF66pfDefTs :: ByteString -> ProgramFile A0 -pF66pfDefTs = parseFull F66.fortran66Parser - -pF66pfNoTs :: ByteString -> ProgramFile A0 -pF66pfNoTs = parseFull (F66.fortran66ParserWithTransforms []) - --- polymorphic on lexer input+token in order to work with fixed & free form -parseFull - :: (ByteString -> String -> ParseResult alexin token (ProgramFile A0)) - -> ByteString -> ProgramFile A0 -parseFull parser bs = - case parser bs "" of - ParseOk a _ -> a - ParseFailed _ -> error "error" - -parseFree :: FortranVersion - -> Parse LexFree.AlexInput LexFree.Token a -> ByteString -> a -parseFree ver parser src = evalParse parser parserState - where parserState = LexFree.initParseState src ver "" +pF90Stmt = Parser.parseUnsafe $ Parser.makeParserFree F90.statementParser Fortran90 -parseFixed :: FortranVersion - -> Parse LexFixed.AlexInput LexFixed.Token a -> ByteString -> a -parseFixed ver parser src = evalParse parser parserState - where parserState = LexFixed.initParseState src ver "" +pF90Func = undefined +pF90pfDefTs = undefined +pF90pfNoTs = undefined +pF77Stmt = undefined +pF77pfDefTs = undefined +pF77pfNoTs = undefined +pF66Stmt = undefined -------------------------------------------------------------------------------- @@ -165,4 +117,4 @@ programListing :: [String] -> ByteString programListing = strToByteString . intercalate "\n" strToByteString :: String -> ByteString -strToByteString = TSE.encodeUtf8 . Text.pack +strToByteString = Text.encodeUtf8 . Text.pack diff --git a/bench/issue-190.f b/bench/issue-190.f new file mode 100644 index 00000000..442a270f --- /dev/null +++ b/bench/issue-190.f @@ -0,0 +1,4 @@ + subroutine main + integer*1 x, int1, a1, a2, a3, a4, a5, a6, a7, a8, a9 + x = INT(int1, 2) + end diff --git a/fortran-src.cabal b/fortran-src.cabal index 575c3118..f9f4d5f2 100644 --- a/fortran-src.cabal +++ b/fortran-src.cabal @@ -277,6 +277,25 @@ benchmark bench Paths_fortran_src hs-source-dirs: bench + default-extensions: + EmptyCase + FlexibleContexts + FlexibleInstances + InstanceSigs + MultiParamTypeClasses + PolyKinds + LambdaCase + DerivingStrategies + StandaloneDeriving + DeriveAnyClass + DeriveGeneric + DeriveDataTypeable + DeriveFunctor + DeriveFoldable + DeriveTraversable + DeriveLift + BangPatterns + TupleSections ghc-options: -Wall build-depends: GenericPretty >=1.2.2 && <2 @@ -288,6 +307,7 @@ benchmark bench , criterion ==1.5.* , deepseq ==1.4.* , directory >=1.2 && <2 + , either >=5.0.1.1 && <5.1 , fgl ==5.* , filepath ==1.4.* , fortran-src From cbcfd1a670e4d40ca384825259e590ba66f94b69 Mon Sep 17 00:00:00 2001 From: Ben Orchard Date: Thu, 3 Feb 2022 12:37:49 +0000 Subject: [PATCH 7/7] tmp --- bench/Bench.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index dbd8aabe..89533710 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -2,17 +2,16 @@ import Criterion.Main -import qualified Language.Fortran.Parser as Parser +import qualified Language.Fortran.Parser as Parser import Language.Fortran.Version import Language.Fortran.AST +import qualified Language.Fortran.Parser.Free.Fortran90 as F90 import Data.ByteString (ByteString) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import Data.List (intercalate) -import qualified Language.Fortran.Parser.Free.Fortran90 as F90 - main :: IO () main = defaultMain [ bgroup "parser"