summaryrefslogtreecommitdiff
path: root/bench/compiler/Main.hs
diff options
context:
space:
mode:
Diffstat (limited to 'bench/compiler/Main.hs')
-rw-r--r--bench/compiler/Main.hs176
1 files changed, 176 insertions, 0 deletions
diff --git a/bench/compiler/Main.hs b/bench/compiler/Main.hs
new file mode 100644
index 000000000..a56a89848
--- /dev/null
+++ b/bench/compiler/Main.hs
@@ -0,0 +1,176 @@
+module Main where
+
+import qualified GF
+import Test.Tasty.Bench
+
+import Control.Exception (catch)
+import Control.Monad (filterM, unless, when)
+import Data.Char (isAlphaNum, toLower, toUpper)
+import System.Directory
+ ( createDirectoryIfMissing
+ , doesDirectoryExist
+ , doesFileExist
+ , getCurrentDirectory
+ , removeFile
+ )
+import System.Environment (lookupEnv, withArgs)
+import System.FilePath ((</>), (<.>), splitSearchPath)
+import System.FilePath (takeBaseName)
+import System.IO.Error (isDoesNotExistError)
+
+data BenchConfig = BenchConfig
+ { benchLabel :: String
+ , benchOutputName :: String
+ , benchGfoDir :: FilePath
+ , benchPgfDir :: FilePath
+ , benchModules :: [FilePath]
+ , benchImportDirs :: [FilePath]
+ }
+
+main :: IO ()
+main = do
+ configs <- readBenchConfigs
+ mapM_ validateBenchConfig configs
+ mapM_ prepareOutputDirs configs
+ defaultMain
+ [ bgroup "GF compiler"
+ [ bench (benchLabel config) $ whnfIO (runCompiler config)
+ | config <- configs
+ ]
+ ]
+
+readBenchConfigs :: IO [BenchConfig]
+readBenchConfigs = do
+ cwd <- getCurrentDirectory
+ rglDir <- lookupEnvDefault "GF_BENCH_RGL_DIR" (".." </> "gf-rgl")
+ let benchDir = cwd </> "profile" </> "rgl-bench"
+ defaultImportDirs = [rglDir </> "src" </> "api"]
+ outputName <- lookupEnvDefault "GF_BENCH_NAME" "RGLBench"
+ gfoDir <- lookupEnvDefault "GF_BENCH_GFO_DIR" (benchDir </> "gfo")
+ pgfDir <- lookupEnvDefault "GF_BENCH_PGF_DIR" (benchDir </> "pgf")
+ importDirs <- lookupEnvList "GF_BENCH_IMPORT_DIRS" defaultImportDirs
+ customModules <- lookupEnvList "GF_BENCH_MODULES" []
+ return $
+ if null customModules
+ then map (defaultBenchConfig rglDir outputName gfoDir pgfDir importDirs)
+ defaultRGLBenchmarks
+ else customBenchConfigs outputName gfoDir pgfDir importDirs customModules
+
+defaultRGLBenchmarks :: [(String, String, FilePath)]
+defaultRGLBenchmarks =
+ [ ("English", "english", "english" </> "LangEng.gf")
+ , ("German", "german", "german" </> "LangGer.gf")
+ , ("French", "french", "french" </> "LangFre.gf")
+ , ("Russian", "russian", "russian" </> "LangRus.gf")
+ , ("Finnish", "finnish", "finnish" </> "LangFin.gf")
+ ]
+
+defaultBenchConfig
+ :: FilePath -> String -> FilePath -> FilePath -> [FilePath]
+ -> (String, String, FilePath)
+ -> BenchConfig
+defaultBenchConfig rglDir outputName gfoDir pgfDir importDirs (language, slug, modulePath) =
+ BenchConfig
+ { benchLabel = "RGL " ++ language ++ " compile/link"
+ , benchOutputName = outputNameFor outputName slug
+ , benchGfoDir = gfoDir </> slug
+ , benchPgfDir = pgfDir </> slug
+ , benchModules = [rglDir </> "src" </> modulePath]
+ , benchImportDirs = importDirs
+ }
+
+customBenchConfigs :: String -> FilePath -> FilePath -> [FilePath] -> [FilePath] -> [BenchConfig]
+customBenchConfigs outputName gfoDir pgfDir importDirs modules =
+ [ BenchConfig
+ { benchLabel = "RGL " ++ takeBaseName modulePath ++ " compile/link"
+ , benchOutputName = caseOutputName modulePath
+ , benchGfoDir = caseDir gfoDir modulePath
+ , benchPgfDir = caseDir pgfDir modulePath
+ , benchModules = [modulePath]
+ , benchImportDirs = importDirs
+ }
+ | modulePath <- modules
+ ]
+ where
+ multiple = length modules > 1
+
+ caseOutputName modulePath
+ | multiple = outputNameFor outputName (takeBaseName modulePath)
+ | otherwise = outputName
+
+ caseDir dir modulePath
+ | multiple = dir </> slugify (takeBaseName modulePath)
+ | otherwise = dir
+
+lookupEnvDefault :: String -> String -> IO String
+lookupEnvDefault key fallback = do
+ value <- lookupEnv key
+ return $ case value of
+ Just xs | not (null xs) -> xs
+ _ -> fallback
+
+lookupEnvList :: String -> [FilePath] -> IO [FilePath]
+lookupEnvList key fallback = do
+ value <- lookupEnv key
+ return $ case value of
+ Just xs | not (null xs) -> parsePathList xs
+ _ -> fallback
+
+parsePathList :: String -> [FilePath]
+parsePathList =
+ filter (not . null) . concatMap splitSearchPath . words
+
+validateBenchConfig :: BenchConfig -> IO ()
+validateBenchConfig config = do
+ when (null (benchModules config)) $
+ fail "GF_BENCH_MODULES did not contain any input modules."
+ missingModules <- filterM (fmap not . doesFileExist) (benchModules config)
+ unless (null missingModules) $
+ fail $ "Missing GF benchmark module(s): " ++ unwords missingModules
+ missingImportDirs <- filterM (fmap not . doesDirectoryExist) (benchImportDirs config)
+ unless (null missingImportDirs) $
+ fail $ "Missing GF benchmark import dir(s): " ++ unwords missingImportDirs
+
+prepareOutputDirs :: BenchConfig -> IO ()
+prepareOutputDirs config = do
+ createDirectoryIfMissing True (benchGfoDir config)
+ createDirectoryIfMissing True (benchPgfDir config)
+
+runCompiler :: BenchConfig -> IO ()
+runCompiler config = do
+ prepareOutputDirs config
+ removeFileIfExists (benchPgfDir config </> benchOutputName config <.> "pgf")
+ withArgs (compilerArgs config) GF.main
+
+compilerArgs :: BenchConfig -> [String]
+compilerArgs config =
+ [ "--make"
+ , "--quiet"
+ , "--src"
+ , "--preproc=mkPresent"
+ , "--gf-lib-path=" ++ benchGfoDir config
+ , "--gfo-dir=" ++ benchGfoDir config
+ , "--output-dir=" ++ benchPgfDir config
+ , "--name=" ++ benchOutputName config
+ ]
+ ++ concatMap (\dir -> ["-i", dir]) (benchImportDirs config)
+ ++ benchModules config
+
+removeFileIfExists :: FilePath -> IO ()
+removeFileIfExists path =
+ removeFile path `catch` \err ->
+ unless (isDoesNotExistError err) (ioError err)
+
+outputNameFor :: String -> String -> String
+outputNameFor outputName slug = outputName ++ capitalize (filter isAlphaNum slug)
+
+capitalize :: String -> String
+capitalize [] = []
+capitalize (x:xs) = toUpper x : xs
+
+slugify :: String -> String
+slugify = map clean
+ where
+ clean c
+ | isAlphaNum c = toLower c
+ | otherwise = '-'