From 99375bb0e61079a6b0937c46b46e0c9c6103c508 Mon Sep 17 00:00:00 2001 From: Alexander Coronel Date: Fri, 12 Jun 2026 17:22:31 -0500 Subject: [PATCH 1/5] Add massiv vs accelarate blog code --- haskell/massiv-vs-accelerate/CHANGELOG.md | 5 + haskell/massiv-vs-accelerate/Dockerfile | 37 +++++ haskell/massiv-vs-accelerate/README.md | 133 ++++++++++++++++++ .../app/BasicComputation.hs | 31 ++++ haskell/massiv-vs-accelerate/app/Main.hs | 129 +++++++++++++++++ haskell/massiv-vs-accelerate/cabal.project | 22 +++ .../massiv-vs-accelerate.cabal | 82 +++++++++++ 7 files changed, 439 insertions(+) create mode 100644 haskell/massiv-vs-accelerate/CHANGELOG.md create mode 100644 haskell/massiv-vs-accelerate/Dockerfile create mode 100644 haskell/massiv-vs-accelerate/README.md create mode 100644 haskell/massiv-vs-accelerate/app/BasicComputation.hs create mode 100644 haskell/massiv-vs-accelerate/app/Main.hs create mode 100644 haskell/massiv-vs-accelerate/cabal.project create mode 100644 haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal diff --git a/haskell/massiv-vs-accelerate/CHANGELOG.md b/haskell/massiv-vs-accelerate/CHANGELOG.md new file mode 100644 index 0000000..930165a --- /dev/null +++ b/haskell/massiv-vs-accelerate/CHANGELOG.md @@ -0,0 +1,5 @@ +# Revision history for massiv-vs-accelerate + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world. diff --git a/haskell/massiv-vs-accelerate/Dockerfile b/haskell/massiv-vs-accelerate/Dockerfile new file mode 100644 index 0000000..baed0d5 --- /dev/null +++ b/haskell/massiv-vs-accelerate/Dockerfile @@ -0,0 +1,37 @@ +# We use GHC 9.6 as our base +FROM haskell:9.6-slim + +# Install wget and gnupg to add the LLVM repository +RUN apt-get update && apt-get install -y \ + wget \ + gnupg \ + lsb-release \ + software-properties-common \ + && rm -rf /var/lib/apt/lists/* + +# Use the official LLVM setup script to install LLVM 15 +# This handles the repository addition for any Debian version (Buster/Bullseye/Bookworm) +RUN wget https://apt.llvm.org/llvm.sh \ + && chmod +x llvm.sh \ + && ./llvm.sh 15 \ + && apt-get install -y \ + llvm-15-dev \ + libffi-dev \ + libedit-dev \ + libfftw3-dev \ + pkg-config \ + zlib1g-dev \ + g++ \ + make \ + && rm -rf /var/lib/apt/lists/* + +# THE FIXES: +# 1. Create the symlink so 'clang' exists in the PATH +RUN ln -s /usr/bin/clang-15 /usr/bin/clang + +# 2. Set the environment variables globally for the container +ENV LLVM_CONFIG=/usr/bin/llvm-config-15 +ENV ACCELERATE_LLVM_CLANG_PATH=/usr/bin/clang-15 + +WORKDIR /app +RUN cabal update \ No newline at end of file diff --git a/haskell/massiv-vs-accelerate/README.md b/haskell/massiv-vs-accelerate/README.md new file mode 100644 index 0000000..663f857 --- /dev/null +++ b/haskell/massiv-vs-accelerate/README.md @@ -0,0 +1,133 @@ +# Massiv vs Accelerate Benchmark Suite + +This project benchmarks and compares the performance of [Massiv](https://github.com/lehins/massiv) and [Accelerate](https://github.com/AccelerateHS/accelerate) Haskell array libraries using a variety of operations (map, dot product, stencil/mean blur). + +## Project Structure + +- `app/Main.hs` — Main benchmarking entry point (Criterion-based) +- `app/BasicComputation.hs` — Additional benchmark/support module +- `Dockerfile` — Containerized build and run environment +- `cabal.project`, `massiv-vs-accelerate.cabal` — Cabal build configuration + +## Prerequisites + +- [Docker](https://www.docker.com/get-started) (recommended for reproducibility) +- Or: [GHC](https://www.haskell.org/ghc/), [Cabal](https://www.haskell.org/cabal/) + +## Quick Start (Docker) + +The `Dockerfile` provides a reproducible Haskell/LLVM build environment. + +Recommended workflow: run an interactive container with the project mounted at `/app`, then execute benchmarks from inside the container. + +1. **Build the Docker image:** + + ```sh + docker build -t massiv-vs-accelerate . + ``` + +2. **Start an interactive container:** + + ```sh + docker run --rm -it \ + --name massiv-vs-accelerate-dev \ + -v "$PWD":/app \ + -w /app \ + massiv-vs-accelerate \ + bash + ``` + +3. **Run benchmarks inside the container shell:** + + ```sh + cabal run massiv-vs-accelerate -- stencil + ``` + + Example with RTS stats: + + ```sh + cabal run massiv-vs-accelerate -- stencil-massiv +RTS -s + ``` + +4. **If the container is already running, attach with `exec`:** + + ```sh + docker exec -it massiv-vs-accelerate-dev bash + ``` + + Or by container id: + + ```sh + docker exec -it bash + ``` + + You can also run other benchmarks: + + - Map: `cabal run massiv-vs-accelerate -- map` + - Dot Product: `cabal run massiv-vs-accelerate -- dot` + - Stencil (Massiv only): `cabal run massiv-vs-accelerate -- stencil-massiv` + - Stencil (Accelerate only): `cabal run massiv-vs-accelerate -- stencil-accel` + + You can pass additional [Criterion](https://hackage.haskell.org/package/criterion) flags after the benchmark type, e.g.: + + ```sh + cabal run massiv-vs-accelerate -- stencil --output results.html + ``` + + One-shot alternative (without opening a shell): + + ```sh + docker run --rm -v "$PWD":/app -w /app massiv-vs-accelerate \ + bash -lc "cabal run massiv-vs-accelerate -- stencil" + ``` + +## Manual Setup (Without Docker) + +1. **Install dependencies:** + + ```sh + cabal update + cabal build --only-dependencies + ``` + +2. **Build the project:** + + ```sh + cabal build + ``` + +3. **Run a benchmark:** + + ```sh + cabal run massiv-vs-accelerate -- [map|dot|stencil|stencil-massiv|stencil-accel] [criterion flags] + ``` + + Example: + + ```sh + cabal run massiv-vs-accelerate -- stencil + ``` + +## Benchmark Types + +- `map` — Map (+1) over a large array +- `dot` — Dot product of two large arrays +- `stencil` — 3x3 mean blur (Massiv vs Accelerate) +- `stencil-massiv` — Only Massiv stencil benchmark +- `stencil-accel` — Only Accelerate stencil benchmark + +## Notes + +- For large benchmarks, you may want to pass RTS flags for memory and GC stats, e.g.: + ```sh + cabal run massiv-vs-accelerate -- stencil +RTS -s + ``` +- The Docker image uses `haskell:9.6-slim`, installs LLVM 15, and sets: + - `LLVM_CONFIG=/usr/bin/llvm-config-15` + - `ACCELERATE_LLVM_CLANG_PATH=/usr/bin/clang-15` +- `cabal.project` pins Accelerate dependencies to GitHub `master` branches (`accelerate` and `accelerate-llvm` repos). +- If you see `The program 'ghc' ... could not be found`, the command is likely running outside the Docker environment. Confirm you are either: + - inside the container shell (`root@...:/app#`), or + - invoking commands through `docker run ... bash -lc "..."`. + + diff --git a/haskell/massiv-vs-accelerate/app/BasicComputation.hs b/haskell/massiv-vs-accelerate/app/BasicComputation.hs new file mode 100644 index 0000000..89a95a9 --- /dev/null +++ b/haskell/massiv-vs-accelerate/app/BasicComputation.hs @@ -0,0 +1,31 @@ +module BasicComputation ( + benchBasicComputation, + BenchArgs(..) +) where + +import Criterion.Main (Benchmark, bench, nf, whnf) +import qualified Data.Massiv.Array as M +import qualified Data.Array.Accelerate as A +import qualified Data.Array.Accelerate.LLVM.Native as Native + +-- | Arguments for benchmarking basic computation +-- Add more fields as needed for future extensibility +-- For now, only size is used + +data BenchArgs = BenchArgs + { size :: Int + } + +benchBasicComputation :: BenchArgs -> [Benchmark] +benchBasicComputation args = + let n = size args + -- Massiv Setup (Parallel Array) + massivArr = M.makeArray M.Par (M.Sz1 n) id :: M.Array M.P M.Ix1 Int + -- Accelerate Setup (Expression Graph) + accelArr = A.fromList (A.Z A.:. n) [0..n-1] :: A.Vector Int + in + [ bench "Massiv (CPU Parallel)" $ + nf (M.computeAs M.P . M.map (+1)) massivArr + , bench "Accelerate (LLVM Native)" $ + whnf (Native.run . A.map (+1) . A.use) accelArr + ] diff --git a/haskell/massiv-vs-accelerate/app/Main.hs b/haskell/massiv-vs-accelerate/app/Main.hs new file mode 100644 index 0000000..4b1161f --- /dev/null +++ b/haskell/massiv-vs-accelerate/app/Main.hs @@ -0,0 +1,129 @@ +module Main where + +import Criterion.Main +import System.Environment (getArgs, withArgs) +import qualified Data.Massiv.Array as M +import qualified Data.Array.Accelerate as A +import qualified Data.Array.Accelerate.LLVM.Native as Native + + +-- | 3x3 mean (average) stencil for 2D arrays in Massiv. +-- +-- This stencil computes the average of a 3x3 neighborhood around each element. +-- It is used for mean blurring (smoothing) operations on 2D arrays. +-- +-- The stencil uses zero-padding (see usage with 'M.Fill 0.0') at the boundaries. +-- +-- Example usage: +-- +-- > M.mapStencil (M.Fill 0.0) massivMeanStencil arr +-- +-- Where 'arr' is a 2D Massiv array of type 'M.Array M.P M.Ix2 Double'. +massivMeanStencil :: M.Stencil M.Ix2 Double Double +massivMeanStencil = M.makeStencil (M.Sz2 3 3) (1 M.:. 1) $ \get -> + ( get (-1 M.:. -1) + get (-1 M.:. 0) + get (-1 M.:. 1) + + get ( 0 M.:. -1) + get ( 0 M.:. 0) + get ( 0 M.:. 1) + + get ( 1 M.:. -1) + get ( 1 M.:. 0) + get ( 1 M.:. 1) + ) / 9 + +-- | 3x3 mean (average) stencil function for Accelerate. +-- +-- This function computes the average of a 3x3 neighborhood for use with Accelerate's 'stencil' operation. +-- It is typically used for mean blurring (smoothing) on 2D arrays. +-- +-- The input is a 3x3 tuple of values (top-left, top-center, top-right, etc.). +-- +-- Example usage: +-- +-- > A.stencil accelMeanStencil A.clamp arr +-- +-- Where 'arr' is an Accelerate 2D array of type 'A.Array A.DIM2 Double'. +accelMeanStencil :: A.Stencil3x3 Double -> A.Exp Double +accelMeanStencil ((tl, tc, tr), + (ml, mc, mr), + (bl, bc, br)) = (tl + tc + tr + ml + mc + mr + bl + bc + br) / 9 + +size :: Int +size = 10000000 + +main :: IO () +main = do + allArgs <- getArgs + case allArgs of + (testType:rest) -> + -- 'withArgs' replaces the global command-line arguments + -- for the duration of the provided IO action. + withArgs rest $ case testType of + "map" -> runMapBenchmarks + "dot" -> runDotBenchmarks + "stencil-massiv" -> runMassivOnly -- Run only the Massiv stencil benchmark with +RTS -s flag + "stencil-accel" -> runAccelOnly -- Run only the Accelerate stencil benchmark with +RTS -s flag + "stencil" -> runStencilBenchmarks + _ -> putStrLn "Unknown test type. Use 'map', 'dot', or 'stencil'." + _ -> putStrLn "Usage: cabal run massiv-vs-accelerate -- [map|dot|stencil] [criterion flags]" + +runMapBenchmarks :: IO () +runMapBenchmarks = do + let massivArr = M.makeArray M.Par (M.Sz1 size) id :: M.Array M.P M.Ix1 Int + let accelArr = A.fromList (A.Z A.:. size) [0..size-1] :: A.Vector Int + + defaultMain [ + bgroup "Map (+1)" [ + bench "Massiv (CPU Parallel)" $ + nf (M.computeAs M.P . M.map (+1)) massivArr, + bench "Accelerate (LLVM Native)" $ + whnf (Native.run . A.map (+1) . A.use) accelArr + ] + ] + +runDotBenchmarks :: IO () +runDotBenchmarks = do + let m1 = M.makeArray M.Par (M.Sz1 size) (const 2) :: M.Array M.P M.Ix1 Int + let m2 = M.makeArray M.Par (M.Sz1 size) (const 3) :: M.Array M.P M.Ix1 Int + + let a1 = A.fromList (A.Z A.:. size) (replicate size 2) :: A.Vector Int + let a2 = A.fromList (A.Z A.:. size) (replicate size 3) :: A.Vector Int + + defaultMain [ + bgroup "Dot Product" [ + bench "Massiv (CPU Parallel)" $ + nf (\(x, y) -> M.sum (M.zipWith (*) x y)) (m1, m2), + bench "Accelerate (LLVM Native)" $ + whnf (\(x, y) -> Native.run (A.fold (+) 0 (A.zipWith (*) (A.use x) (A.use y)))) (a1, a2) + ] + ] + +runStencilBenchmarks :: IO () +runStencilBenchmarks = do + let side = 3162 + let sz = M.Sz2 side side + + -- Massiv Setup + let mArr = M.makeArray M.Par sz (\(i M.:. j) -> fromIntegral (i + j)) :: M.Array M.P M.Ix2 Double + + -- Accelerate Setup + let aArr = A.fromList (A.Z A.:. side A.:. side) [0..(fromIntegral (side*side - 1))] :: A.Array A.DIM2 Double + + defaultMain [ + bgroup "3x3 Mean Blur" [ + bench "Massiv (Stencil)" $ + nf (M.computeAs M.P . M.mapStencil (M.Fill 0.0) massivMeanStencil) mArr, + + bench "Accelerate (LLVM Stencil)" $ + whnf (Native.run . A.stencil accelMeanStencil A.clamp . A.use) aArr + ] + ] + +runMassivOnly :: IO () +runMassivOnly = do + let side = 3162 + let sz = M.Sz2 side side + let mArr = M.makeArray M.Par sz (\(i M.:. j) -> fromIntegral (i + j)) :: M.Array M.P M.Ix2 Double + defaultMain [ bench "Massiv (Isolated)" $ nf (M.computeAs M.P . M.mapStencil (M.Fill 0.0) massivMeanStencil) mArr ] + +runAccelOnly :: IO () +runAccelOnly = do + let side = 3162 + let aArr = A.fromList (A.Z A.:. side A.:. side) [0..(fromIntegral (side*side - 1))] :: A.Array A.DIM2 Double + defaultMain [ bench "Accelerate (Isolated)" $ whnf (Native.run . A.stencil accelMeanStencil A.clamp . A.use) aArr ] + diff --git a/haskell/massiv-vs-accelerate/cabal.project b/haskell/massiv-vs-accelerate/cabal.project new file mode 100644 index 0000000..e83fa70 --- /dev/null +++ b/haskell/massiv-vs-accelerate/cabal.project @@ -0,0 +1,22 @@ +packages: . + +source-repository-package + type: git + location: https://github.com/AccelerateHS/accelerate.git + tag: master + +source-repository-package + type: git + location: https://github.com/AccelerateHS/accelerate-llvm.git + tag: master + -- This tells Cabal to look inside these specific folders for the .cabal files + subdir: + accelerate-llvm + accelerate-llvm-native + +allow-newer: + *:bytestring, + *:base, + *:template-haskell, + llvm-hs:llvm-hs-pure, + llvm-hs-pure:base \ No newline at end of file diff --git a/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal new file mode 100644 index 0000000..de0635a --- /dev/null +++ b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal @@ -0,0 +1,82 @@ +cabal-version: 3.14 +-- The cabal-version field refers to the version of the .cabal specification, +-- and can be different from the cabal-install (the tool) version and the +-- Cabal (the library) version you are using. As such, the Cabal (the library) +-- version used must be equal or greater than the version stated in this field. +-- Starting from the specification version 2.2, the cabal-version field must be +-- the first thing in the cabal file. + +-- Initial package description 'massiv-vs-accelerate' generated by +-- 'cabal init'. For further documentation, see: +-- http://haskell.org/cabal/users-guide/ +-- +-- The name of the package. +name: massiv-vs-accelerate + +-- The package version. +-- See the Haskell package versioning policy (PVP) for standards +-- guiding when and how versions should be incremented. +-- https://pvp.haskell.org +-- PVP summary: +-+------- breaking API changes +-- | | +----- non-breaking API additions +-- | | | +--- code changes with no API change +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- The license under which the package is released. +license: NONE + +-- The package author(s). +author: Alexander Coronel + +-- An email address to which users can send suggestions, bug reports, and patches. +maintainer: alexcoronel1995@gmail.com + +-- A copyright notice. +-- copyright: +build-type: Simple + +-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README. +extra-doc-files: CHANGELOG.md + +-- Extra source files to be distributed with the package, such as examples, or a tutorial module. +-- extra-source-files: + +common warnings + ghc-options: -Wall + + +executable massiv-vs-accelerate + -- Import common warning flags. + import: warnings + + -- .hs or .lhs file containing the Main module. + main-is: Main.hs + + -- Modules included in this executable, other than Main. + other-modules: BasicComputation + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + + -- Other library packages from which modules are imported. + build-depends: + base ^>=4.18.3.0, + massiv, + accelerate, + accelerate-llvm-native, + vector, + criterion, + optparse-applicative + + -- Directories containing source files. + hs-source-dirs: app + + -- Base language which the package is written in. + default-language: Haskell2010 + ghc-options: -threaded -O2 -with-rtsopts=-N From d461fbdefaa8922dfa6f6e2fc6f87fee1633ceaa Mon Sep 17 00:00:00 2001 From: Alexander Coronel Date: Mon, 6 Jul 2026 12:16:16 -0500 Subject: [PATCH 2/5] update compiler flags, and evaluate exp with nf --- haskell/massiv-vs-accelerate/README.md | 64 +++---------------- haskell/massiv-vs-accelerate/app/Main.hs | 2 +- .../massiv-vs-accelerate.cabal | 10 ++- 3 files changed, 19 insertions(+), 57 deletions(-) diff --git a/haskell/massiv-vs-accelerate/README.md b/haskell/massiv-vs-accelerate/README.md index 663f857..0ae562d 100644 --- a/haskell/massiv-vs-accelerate/README.md +++ b/haskell/massiv-vs-accelerate/README.md @@ -5,7 +5,7 @@ This project benchmarks and compares the performance of [Massiv](https://github. ## Project Structure - `app/Main.hs` — Main benchmarking entry point (Criterion-based) -- `app/BasicComputation.hs` — Additional benchmark/support module +- `src/` — Library and dependency sources (submodules) - `Dockerfile` — Containerized build and run environment - `cabal.project`, `massiv-vs-accelerate.cabal` — Cabal build configuration @@ -16,69 +16,29 @@ This project benchmarks and compares the performance of [Massiv](https://github. ## Quick Start (Docker) -The `Dockerfile` provides a reproducible Haskell/LLVM build environment. - -Recommended workflow: run an interactive container with the project mounted at `/app`, then execute benchmarks from inside the container. - 1. **Build the Docker image:** ```sh docker build -t massiv-vs-accelerate . ``` -2. **Start an interactive container:** - - ```sh - docker run --rm -it \ - --name massiv-vs-accelerate-dev \ - -v "$PWD":/app \ - -w /app \ - massiv-vs-accelerate \ - bash - ``` - -3. **Run benchmarks inside the container shell:** +2. **Run a benchmark (e.g., stencil):** ```sh - cabal run massiv-vs-accelerate -- stencil - ``` - - Example with RTS stats: - - ```sh - cabal run massiv-vs-accelerate -- stencil-massiv +RTS -s - ``` - -4. **If the container is already running, attach with `exec`:** - - ```sh - docker exec -it massiv-vs-accelerate-dev bash - ``` - - Or by container id: - - ```sh - docker exec -it bash + docker run --rm -it massiv-vs-accelerate bash -c "cabal run massiv-vs-accelerate -- stencil" ``` You can also run other benchmarks: - - Map: `cabal run massiv-vs-accelerate -- map` - - Dot Product: `cabal run massiv-vs-accelerate -- dot` - - Stencil (Massiv only): `cabal run massiv-vs-accelerate -- stencil-massiv` - - Stencil (Accelerate only): `cabal run massiv-vs-accelerate -- stencil-accel` + - Map: `docker run --rm massiv-vs-accelerate map` + - Dot Product: `docker run --rm massiv-vs-accelerate dot` + - Stencil (Massiv only): `docker run --rm massiv-vs-accelerate stencil-massiv` + - Stencil (Accelerate only): `docker run --rm massiv-vs-accelerate stencil-accel` You can pass additional [Criterion](https://hackage.haskell.org/package/criterion) flags after the benchmark type, e.g.: ```sh - cabal run massiv-vs-accelerate -- stencil --output results.html - ``` - - One-shot alternative (without opening a shell): - - ```sh - docker run --rm -v "$PWD":/app -w /app massiv-vs-accelerate \ - bash -lc "cabal run massiv-vs-accelerate -- stencil" + docker run --rm massiv-vs-accelerate stencil --output results.html ``` ## Manual Setup (Without Docker) @@ -122,12 +82,6 @@ Recommended workflow: run an interactive container with the project mounted at ` ```sh cabal run massiv-vs-accelerate -- stencil +RTS -s ``` -- The Docker image uses `haskell:9.6-slim`, installs LLVM 15, and sets: - - `LLVM_CONFIG=/usr/bin/llvm-config-15` - - `ACCELERATE_LLVM_CLANG_PATH=/usr/bin/clang-15` -- `cabal.project` pins Accelerate dependencies to GitHub `master` branches (`accelerate` and `accelerate-llvm` repos). -- If you see `The program 'ghc' ... could not be found`, the command is likely running outside the Docker environment. Confirm you are either: - - inside the container shell (`root@...:/app#`), or - - invoking commands through `docker run ... bash -lc "..."`. +- The Docker image uses the default system GHC and Cabal versions. For custom builds, edit the `Dockerfile`. diff --git a/haskell/massiv-vs-accelerate/app/Main.hs b/haskell/massiv-vs-accelerate/app/Main.hs index 4b1161f..50393fd 100644 --- a/haskell/massiv-vs-accelerate/app/Main.hs +++ b/haskell/massiv-vs-accelerate/app/Main.hs @@ -110,7 +110,7 @@ runStencilBenchmarks = do nf (M.computeAs M.P . M.mapStencil (M.Fill 0.0) massivMeanStencil) mArr, bench "Accelerate (LLVM Stencil)" $ - whnf (Native.run . A.stencil accelMeanStencil A.clamp . A.use) aArr + nf (Native.run . A.stencil accelMeanStencil A.clamp . A.use) aArr ] ] diff --git a/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal index de0635a..87e1fda 100644 --- a/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal +++ b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal @@ -79,4 +79,12 @@ executable massiv-vs-accelerate -- Base language which the package is written in. default-language: Haskell2010 - ghc-options: -threaded -O2 -with-rtsopts=-N + ghc-options: -O2 + -threaded + -rtsopts + -with-rtsopts=-N + -fllvm + -optlo-O3 + -fno-liberate-case + -funfolding-use-threshold=100 + -funfolding-keeness-factor=100 \ No newline at end of file From 0abab786780ab562ae573d1f57661bcf0a73677a Mon Sep 17 00:00:00 2001 From: Alexander Coronel Date: Mon, 6 Jul 2026 14:57:07 -0500 Subject: [PATCH 3/5] update docker, readme and add instructions to run without docker (native on target machine) --- haskell/massiv-vs-accelerate/Dockerfile | 31 ++++- haskell/massiv-vs-accelerate/README.md | 108 ++++++++++-------- .../massiv-vs-accelerate.cabal | 3 +- 3 files changed, 86 insertions(+), 56 deletions(-) diff --git a/haskell/massiv-vs-accelerate/Dockerfile b/haskell/massiv-vs-accelerate/Dockerfile index baed0d5..839ac11 100644 --- a/haskell/massiv-vs-accelerate/Dockerfile +++ b/haskell/massiv-vs-accelerate/Dockerfile @@ -15,6 +15,8 @@ RUN wget https://apt.llvm.org/llvm.sh \ && chmod +x llvm.sh \ && ./llvm.sh 15 \ && apt-get install -y \ + llvm \ + clang \ llvm-15-dev \ libffi-dev \ libedit-dev \ @@ -26,12 +28,31 @@ RUN wget https://apt.llvm.org/llvm.sh \ && rm -rf /var/lib/apt/lists/* # THE FIXES: -# 1. Create the symlink so 'clang' exists in the PATH -RUN ln -s /usr/bin/clang-15 /usr/bin/clang +# 1. Create the symlinks so GHC and Accelerate tools exist cleanly in the PATH +# We use -sf to force overwrite existing default paths safely +RUN ln -sf /usr/bin/clang-15 /usr/bin/clang && \ + ln -sf /usr/bin/opt-15 /usr/bin/opt && \ + ln -sf /usr/bin/llc-15 /usr/bin/llc && \ + ln -sf /usr/bin/llvm-config-15 /usr/bin/llvm-config # 2. Set the environment variables globally for the container -ENV LLVM_CONFIG=/usr/bin/llvm-config-15 -ENV ACCELERATE_LLVM_CLANG_PATH=/usr/bin/clang-15 +ENV LLVM_CONFIG=/usr/bin/llvm-config +ENV ACCELERATE_LLVM_CLANG_PATH=/usr/bin/clang +# Set up the working directory inside the container WORKDIR /app -RUN cabal update \ No newline at end of file + +# Update the package list +RUN cabal update + +# Copy the cabal configuration first to cache dependencies (optimization step) +COPY *.cabal cabal.project /app/ + +# Copy the rest of the application source files +COPY . /app + +# Pre-compile the project inside the container image +RUN cabal build + +# Set the default entry point to execute your compiled benchmark suite +ENTRYPOINT ["cabal", "run", "massiv-vs-accelerate", "--"] \ No newline at end of file diff --git a/haskell/massiv-vs-accelerate/README.md b/haskell/massiv-vs-accelerate/README.md index 0ae562d..328d075 100644 --- a/haskell/massiv-vs-accelerate/README.md +++ b/haskell/massiv-vs-accelerate/README.md @@ -6,82 +6,92 @@ This project benchmarks and compares the performance of [Massiv](https://github. - `app/Main.hs` — Main benchmarking entry point (Criterion-based) - `src/` — Library and dependency sources (submodules) -- `Dockerfile` — Containerized build and run environment +- `Dockerfile` — Containerized build and run environment matching target LLVM pipelines - `cabal.project`, `massiv-vs-accelerate.cabal` — Cabal build configuration ## Prerequisites -- [Docker](https://www.docker.com/get-started) (recommended for reproducibility) -- Or: [GHC](https://www.haskell.org/ghc/), [Cabal](https://www.haskell.org/cabal/) +- [Docker](https://www.docker.com/get-started) (highly recommended for ARM64 toolchain compatibility and reproducibility) +- Or: [GHC 9.6+](https://www.haskell.org/ghc/), [Cabal](https://www.haskell.org/cabal/), and **LLVM 15** installed with `clang`/`opt`/`llc` binaries mapped to your system path. ## Quick Start (Docker) -1. **Build the Docker image:** +1. **Build the containerized environment:** - ```sh +```sh docker build -t massiv-vs-accelerate . - ``` +``` -2. **Run a benchmark (e.g., stencil):** +2. **Run isolated library benchmarks:** +Pass the specific operation name directly to the container runner to evaluate both libraries back-to-back: - ```sh - docker run --rm -it massiv-vs-accelerate bash -c "cabal run massiv-vs-accelerate -- stencil" - ``` +Massiv Stencil Only: +```bash +docker run --rm massiv-vs-accelerate stencil-massiv +``` +Accelerate Stencil Only: - You can also run other benchmarks: +```bash +docker run --rm massiv-vs-accelerate stencil-accel +``` - - Map: `docker run --rm massiv-vs-accelerate map` - - Dot Product: `docker run --rm massiv-vs-accelerate dot` - - Stencil (Massiv only): `docker run --rm massiv-vs-accelerate stencil-massiv` - - Stencil (Accelerate only): `docker run --rm massiv-vs-accelerate stencil-accel` +3. **Passing Criterion and GHC Runtime (+RTS) Flags:** - You can pass additional [Criterion](https://hackage.haskell.org/package/criterion) flags after the benchmark type, e.g.: +Because the container features an automated entrypoint wrapper, you can append reporting flags or memory profiling switches straight to your commands: - ```sh - docker run --rm massiv-vs-accelerate stencil --output results.html - ``` +```bash +# Export an interactive HTML chart to your current host directory +docker run --rm -v $(pwd):/app/out massiv-vs-accelerate stencil --output out/results.html -## Manual Setup (Without Docker) +# Inspect memory allocation and GC productivity profiles (+RTS -s) +docker run --rm massiv-vs-accelerate stencil-massiv +RTS -s -1. **Install dependencies:** +``` - ```sh - cabal update - cabal build --only-dependencies - ``` +4. **Manual Setup (Without Docker)** +Configure dependencies: -2. **Build the project:** +Ensure llvm-15 toolchains are [globally](https://www.acceleratehs.org/get-started.html) exposed on your machine. - ```sh - cabal build - ``` +1. Install llvm dependencies -3. **Run a benchmark:** +brew install llvm@15 libffi pkg-config - ```sh - cabal run massiv-vs-accelerate -- [map|dot|stencil|stencil-massiv|stencil-accel] [criterion flags] - ``` +2. Expose the New Binaries to Your Local Terminal +```bash +# 1. Force your active terminal context to prioritize LLVM 15 +export PATH="/opt/homebrew/opt/llvm@15/bin:$PATH" - Example: +# 2. Bind development header search flags +export LDFLAGS="-L/opt/homebrew/opt/llvm@15/lib" +export CPPFLAGS="-I/opt/homebrew/opt/llvm@15/include" - ```sh - cabal run massiv-vs-accelerate -- stencil - ``` +# 3. Explicitly link tool locations for the GHC and Accelerate compiler pipelines +export LLVM_CONFIG="/opt/homebrew/opt/llvm@15/bin/llvm-config" +export ACCELERATE_LLVM_CLANG_PATH="/opt/homebrew/opt/llvm@15/bin/clang" +``` -## Benchmark Types +Trigger the native build +``` +cabal clean && cabal build +``` +Run a benchmark: -- `map` — Map (+1) over a large array -- `dot` — Dot product of two large arrays -- `stencil` — 3x3 mean blur (Massiv vs Accelerate) -- `stencil-massiv` — Only Massiv stencil benchmark -- `stencil-accel` — Only Accelerate stencil benchmark +```Bash +cabal run massiv-vs-accelerate -- [map|dot|stencil|stencil-massiv|stencil-accel] [criterion flags] +``` +Example: -## Notes +```Bash +cabal run massiv-vs-accelerate -- stencil +``` +5. **Benchmark Types** +- map — Map (+1) over a large array -- For large benchmarks, you may want to pass RTS flags for memory and GC stats, e.g.: - ```sh - cabal run massiv-vs-accelerate -- stencil +RTS -s - ``` -- The Docker image uses the default system GHC and Cabal versions. For custom builds, edit the `Dockerfile`. +- dot — Dot product of two large arrays +- stencil — 3x3 mean blur matrix operations comparison +- stencil-massiv — Isolated Massiv stencil loop + +- stencil-accel — Isolated Accelerate LLVM backend stencil \ No newline at end of file diff --git a/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal index 87e1fda..3843c6d 100644 --- a/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal +++ b/haskell/massiv-vs-accelerate/massiv-vs-accelerate.cabal @@ -84,7 +84,6 @@ executable massiv-vs-accelerate -rtsopts -with-rtsopts=-N -fllvm - -optlo-O3 -fno-liberate-case -funfolding-use-threshold=100 - -funfolding-keeness-factor=100 \ No newline at end of file + -pgma clang \ No newline at end of file From cc02a8c8a7426e4655e8415eec7f0d34d4325532 Mon Sep 17 00:00:00 2001 From: Alexander Coronel Date: Mon, 6 Jul 2026 17:32:10 -0500 Subject: [PATCH 4/5] update readme to have instructions for MacOs installation --- haskell/massiv-vs-accelerate/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haskell/massiv-vs-accelerate/README.md b/haskell/massiv-vs-accelerate/README.md index 328d075..99f77db 100644 --- a/haskell/massiv-vs-accelerate/README.md +++ b/haskell/massiv-vs-accelerate/README.md @@ -48,7 +48,7 @@ docker run --rm massiv-vs-accelerate stencil-massiv +RTS -s ``` -4. **Manual Setup (Without Docker)** +4. **Manual Setup (Without Docker on Mac)** Configure dependencies: Ensure llvm-15 toolchains are [globally](https://www.acceleratehs.org/get-started.html) exposed on your machine. From a5d016c37c85416f534af653455e0781ea63c946 Mon Sep 17 00:00:00 2001 From: Alexander Coronel Date: Tue, 28 Jul 2026 08:51:48 -0500 Subject: [PATCH 5/5] add tag with sha commits and cabal freeze --- haskell/massiv-vs-accelerate/cabal.project | 4 +- .../massiv-vs-accelerate/cabal.project.freeze | 200 ++++++++++++++++++ 2 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 haskell/massiv-vs-accelerate/cabal.project.freeze diff --git a/haskell/massiv-vs-accelerate/cabal.project b/haskell/massiv-vs-accelerate/cabal.project index e83fa70..66f2d50 100644 --- a/haskell/massiv-vs-accelerate/cabal.project +++ b/haskell/massiv-vs-accelerate/cabal.project @@ -3,12 +3,12 @@ packages: . source-repository-package type: git location: https://github.com/AccelerateHS/accelerate.git - tag: master + tag: 981db031c7573bd38decbf3c123769a051ce9f32 source-repository-package type: git location: https://github.com/AccelerateHS/accelerate-llvm.git - tag: master + tag: 1b3acb629cf90b0fbcecb985e4349c3055d70ee2 -- This tells Cabal to look inside these specific folders for the .cabal files subdir: accelerate-llvm diff --git a/haskell/massiv-vs-accelerate/cabal.project.freeze b/haskell/massiv-vs-accelerate/cabal.project.freeze new file mode 100644 index 0000000..bf533fe --- /dev/null +++ b/haskell/massiv-vs-accelerate/cabal.project.freeze @@ -0,0 +1,200 @@ +active-repositories: hackage.haskell.org:merge +constraints: any.Cabal ==3.12.1.0, + any.Cabal-syntax ==3.12.1.0, + any.Glob ==0.10.2, + any.OneTuple ==0.4.3, + OneTuple +base-ge-4-15 +base-ge-4-16, + any.Only ==0.1, + any.QuickCheck ==2.18.0.0, + QuickCheck -old-random +templatehaskell, + any.StateVar ==1.2.2, + any.abstract-deque ==0.3, + abstract-deque -usecas, + any.accelerate ==1.4.0.0, + accelerate +bounds-checks -debug -internal-checks -nofib -tracy, + any.accelerate-llvm ==1.4.0.0, + any.accelerate-llvm-native ==1.4.0.0, + any.aeson ==2.2.5.0, + aeson +ordered-keymap, + any.ansi-terminal ==1.1.5, + ansi-terminal -example, + any.ansi-terminal-types ==1.1.3, + any.array ==0.5.8.0, + any.assoc ==1.1.1, + assoc -tagged, + any.async ==2.2.6, + async -bench -debug-auto-label, + any.atomic-primops ==0.8.8, + atomic-primops -debug, + any.attoparsec ==0.14.4, + attoparsec -developer, + any.barbies ==2.1.1.0, + any.base ==4.20.2.0, + any.base-compat ==0.15.0, + any.base-compat-batteries ==0.15.0, + any.base-orphans ==0.9.4, + any.bifunctors ==5.6.3, + bifunctors +tagged, + any.binary ==0.8.9.3, + any.binary-orphans ==1.0.6, + binary-orphans +base-ge-4-16 +base-ge-4-17, + any.bitvec ==1.1.6.0, + bitvec +simd, + any.boring ==0.2.2.1, + boring +tagged, + any.bytestring ==0.12.2.0, + any.cabal-doctest ==1.0.12, + any.cassava ==0.5.4.1, + any.character-ps ==0.1, + any.clock ==0.8.4, + clock -llvm, + any.code-page ==0.2.1, + any.colour ==2.3.7, + any.comonad ==5.0.10, + comonad +containers +distributive +indexed-traversable, + any.concurrent-output ==1.10.21, + any.constraints ==0.14.4, + any.containers ==0.7, + any.contravariant ==1.5.6, + contravariant +statevar, + any.criterion ==1.6.5.0, + criterion -embed-data-files -fast, + any.criterion-measurement ==0.2.5.0, + criterion-measurement -fast, + any.data-default ==0.8.0.2, + any.data-default-class ==0.2.0.0, + any.data-fix ==0.3.4, + any.deepseq ==1.5.0.0, + any.dense-linear-algebra ==0.1.0.0, + any.directory ==1.3.8.5, + any.distributive ==0.6.3, + distributive +tagged, + any.dlist ==1.0, + dlist -werror, + any.double-conversion ==2.0.5.0, + double-conversion -developer +embedded_double_conversion, + any.erf ==2.0.0.0, + any.exceptions ==0.10.9, + any.filepath ==1.5.4.0, + any.formatting ==7.2.0, + formatting -no-double-conversion, + any.ghc ==9.10.3, + any.ghc-bignum ==1.3, + any.ghc-boot ==9.10.3, + any.ghc-boot-th ==9.10.3, + any.ghc-heap ==9.10.3, + any.ghc-internal ==9.1003.0, + any.ghc-platform ==0.1.0.0, + any.ghc-prim ==0.12.0, + any.ghci ==9.10.3, + any.half ==0.3.3, + any.happy ==2.2, + any.happy-lib ==2.2, + any.hashable ==1.5.1.0, + hashable -arch-native -random-initial-seed, + any.hashtables ==1.4.2, + hashtables -bounds-checking -debug -detailed-profiling -portable -sse42 +unsafe-tricks, + any.haskell-lexer ==1.2.1, + any.hedgehog ==1.7, + any.hpc ==0.7.0.2, + any.hsc2hs ==0.68.10, + hsc2hs -in-ghc-tree, + any.indexed-traversable ==0.1.5, + indexed-traversable +base-ge-4-18, + any.indexed-traversable-instances ==0.1.2.1, + any.integer-conversion ==0.1.1, + any.integer-logarithms ==1.0.5, + integer-logarithms -check-bounds +integer-gmp, + any.js-chart ==2.9.4.1, + any.libffi ==0.2.1, + libffi +ghc-bundled-libffi, + any.lifted-async ==0.11.0, + any.lifted-base ==0.2.3.12, + any.lockfree-queue ==0.2.4, + any.massiv ==1.0.5.0, + massiv -unsafe-checks, + any.math-functions ==0.3.4.4, + math-functions +system-erf +system-expm1, + any.microlens ==0.5.0.0, + any.microlens-th ==0.4.3.18, + any.microstache ==1.0.3.1, + any.mmorph ==1.2.2, + any.monad-control ==1.0.3.1, + any.monadLib ==3.10.3, + any.mtl ==2.3.1, + any.mwc-random ==0.15.3.0, + mwc-random -benchpapi, + any.network-uri ==2.6.4.2, + any.old-locale ==1.0.0.7, + any.optparse-applicative ==0.19.0.0, + optparse-applicative +process, + any.os-string ==2.0.7, + any.parallel ==3.3.0.0, + any.parsec ==3.1.18.0, + any.pretty ==1.1.3.6, + any.pretty-show ==1.10, + any.prettyprinter ==1.7.2, + prettyprinter -buildreadme +text, + any.prettyprinter-ansi-terminal ==1.1.4, + prettyprinter-ansi-terminal +text, + any.primitive ==0.9.1.0, + any.process ==1.6.26.1, + any.pvar ==1.0.0.0, + any.random ==1.3.1, + any.resourcet ==1.3.0, + any.rts ==1.0.2, + any.safe-exceptions ==0.1.7.4, + any.scheduler ==2.0.1.0, + any.scientific ==0.3.8.1, + scientific -integer-simple, + any.semaphore-compat ==1.0.0, + any.semialign ==1.4, + semialign +semigroupoids, + any.semigroupoids ==6.0.2, + semigroupoids +comonad +containers +contravariant +tagged +unordered-containers, + any.splitmix ==0.1.3.2, + splitmix -optimised-mixer, + any.statistics ==0.16.5.0, + statistics -benchpapi, + any.stm ==2.5.3.1, + any.strict ==0.5.1, + any.syb ==0.7.4, + any.system-cxx-std-lib ==1.0, + any.tagged ==0.8.10, + tagged +deepseq +template-haskell, + any.tasty ==1.5.4, + tasty +unix, + any.template-haskell ==2.22.0.0, + any.terminal-size ==0.3.4, + any.text ==2.1.3, + any.text-iso8601 ==0.1.1.1, + any.text-short ==0.1.6.1, + text-short -asserts, + any.th-abstraction ==0.7.2.0, + any.th-compat ==0.1.7, + any.th-lift ==0.8.7, + any.th-lift-instances ==0.1.20, + any.these ==1.2.1, + any.time ==1.12.2, + any.time-compat ==1.9.9, + any.transformers ==0.6.1.1, + any.transformers-base ==0.4.6.1, + transformers-base +orphaninstances, + any.transformers-compat ==0.7.2, + transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, + any.unique ==0.0.2, + any.unix ==2.8.7.0, + any.unliftio-core ==0.2.1.0, + any.unordered-containers ==0.2.21, + unordered-containers -debug, + any.uuid-types ==1.0.6.1, + any.vector ==0.13.2.0, + vector +boundschecks -internalchecks -unsafechecks -wall, + any.vector-algorithms ==0.9.1.0, + vector-algorithms +bench +boundschecks -internalchecks -llvm -unsafechecks, + any.vector-binary-instances ==0.2.5.2, + any.vector-stream ==0.1.0.1, + any.vector-th-unbox ==0.2.2, + any.witherable ==0.5, + any.wl-pprint-annotated ==0.1.0.1 +index-state: hackage.haskell.org 2026-07-03T21:00:34Z