packages feed

wasmedge-hs-0.17.1.0: examples/Stress/AOTWorkflow.hs

{-# LANGUAGE OverloadedStrings #-}

{- |
Module      : Stress.AOTWorkflow
Description : Ahead-Of-Time (AOT) compilation and execution benchmark suite
Copyright   : (c) 2026 Tushar Adhatrao
License     : MIT
Stability   : experimental

Tests the full AOT compilation pipeline using 'WasmEdge.Compiler':
  1. Compiling in-memory WebAssembly bytecode to native shared library
  2. Configuring optimization levels (O0 through O3)
  3. Loading native compiled object files into 'VM'
  4. Benchmarking execution speed of AOT native vs Interpreter
  5. Verifying 100% numerical parity between AOT and interpreted runs
-}
module Stress.AOTWorkflow
  ( runAOTWorkflowSuite
  , AOTMetrics (..)
  ) where

import Control.Exception (throwIO)
import Control.Monad (unless)
import System.Clock (Clock (Monotonic), diffTimeSpec, getTime, toNanoSecs)
import System.FilePath ((</>))
import System.IO.Temp (withSystemTempDirectory)
import Text.Printf (printf)

import Stress.WasmBytecode (fibWasm)
import WasmEdge.Compiler (compileBytes, withCompiler)
import WasmEdge.Configure
  ( CompilerOptions (..)
  , defaultCompilerOptions
  , setCompilerOptions
  , withConfigure
  )
import WasmEdge.Types (OptimizationLevel (..), OutputFormat (..), WasmVal (..))
import WasmEdge.VM (runWasmBytes, runWasmFile, withVM)

-- | Metrics recorded during AOT compilation and execution benchmark
data AOTMetrics = AOTMetrics
  { aotCompileDurationSec :: !Double
  , aotInterpreterSec :: !Double
  , aotNativeSec :: !Double
  , aotSpeedupRatio :: !Double
  , aotNumericalParity :: !Bool
  }
  deriving stock (Show)

-- | Run the AOT compilation and execution suite.
runAOTWorkflowSuite :: IO AOTMetrics
runAOTWorkflowSuite = do
  putStrLn "============================================================"
  putStrLn "  STRESS TEST: Ahead-Of-Time (AOT) Compilation & Speedup"
  putStrLn "============================================================"

  let n = 28 :: Int -- fib(28) = 317811
      expectedResult = 317811 :: Int

  withSystemTempDirectory "wasmedge_aot_stress" $ \tempDir -> do
    let aotPath = tempDir </> "fib_aot.so"

    -- 1. Compile fibWasm to native .so at OptO3
    putStrLn "--> [1/3] Compiling fibWasm to native shared library (Optimization Level: OptO3)..."
    tCompile0 <- getTime Monotonic
    compileRes <- withConfigure $ \cfg -> do
      setCompilerOptions
        cfg
        defaultCompilerOptions
          { compOptLevel = OptO3
          , compOutputFormat = OutputNativeSharedLibrary
          }
      withCompiler (Just cfg) $ \compiler ->
        compileBytes compiler fibWasm aotPath
    tCompile1 <- getTime Monotonic

    case compileRes of
      Left err -> throwIO (userError $ "AOT compilation failed: " ++ show err)
      Right () -> pure ()

    let compileSec = fromIntegral (toNanoSecs (diffTimeSpec tCompile1 tCompile0)) / 1e9 :: Double
    printf "    AOT compilation completed in %.3f s -> %s\n" compileSec aotPath

    -- 2. Execute on Interpreter
    putStrLn $ printf "--> [2/3] Executing fib(%d) on Interpreter..." n
    tInterp0 <- getTime Monotonic
    interpRes <- withVM Nothing Nothing $ \vm ->
      runWasmBytes vm fibWasm "fib" [WasmI32 (fromIntegral n)]
    tInterp1 <- getTime Monotonic

    interpVal <- case interpRes of
      Left err -> throwIO (userError $ "Interpreter failed: " ++ show err)
      Right [WasmI32 v] -> pure (fromIntegral v :: Int)
      Right other -> throwIO (userError $ "Unexpected return: " ++ show other)
    let interpSec = fromIntegral (toNanoSecs (diffTimeSpec tInterp1 tInterp0)) / 1e9 :: Double
    printf "    Interpreter returned: %d in %.4f s\n" interpVal interpSec

    -- 3. Execute on AOT Native
    putStrLn $ printf "--> [3/3] Executing fib(%d) on AOT Native object..." n
    tNative0 <- getTime Monotonic
    nativeRes <- withVM Nothing Nothing $ \vm ->
      runWasmFile vm aotPath "fib" [WasmI32 (fromIntegral n)]
    tNative1 <- getTime Monotonic

    nativeVal <- case nativeRes of
      Left err -> throwIO (userError $ "AOT Native failed: " ++ show err)
      Right [WasmI32 v] -> pure (fromIntegral v :: Int)
      Right other -> throwIO (userError $ "Unexpected return: " ++ show other)
    let nativeSec = fromIntegral (toNanoSecs (diffTimeSpec tNative1 tNative0)) / 1e9 :: Double
    printf "    AOT Native returned:  %d in %.4f s\n" nativeVal nativeSec

    -- Parity and Speedup
    let parity = (interpVal == expectedResult) && (nativeVal == expectedResult)
        speedup = if nativeSec > 0 then interpSec / nativeSec else 1.0

    putStrLn $ "    Numerical parity: " ++ if parity then "PASSED (identical)" else "FAILED"
    printf "    AOT Speedup: %.2fx faster than Interpreter\n\n" speedup

    unless parity $
      throwIO (userError "AOT vs Interpreter result mismatch!")

    pure $
      AOTMetrics
        { aotCompileDurationSec = compileSec
        , aotInterpreterSec = interpSec
        , aotNativeSec = nativeSec
        , aotSpeedupRatio = speedup
        , aotNumericalParity = parity
        }