[mpact][benchmark] set up regression benchmark for each commit with graphs (#58)

diff --git a/.github/workflows/regression-benchmark.yml b/.github/workflows/regression-benchmark.yml
new file mode 100644
index 0000000..dd59a99
--- /dev/null
+++ b/.github/workflows/regression-benchmark.yml
@@ -0,0 +1,83 @@
+name: Regression benchmark
+
+on:
+  push:
+    branches: [ "main" ]
+
+permissions:
+  contents: write
+  deployments: write
+  pull-requests: write
+  repository-projects: write
+
+jobs:
+  benchmark:
+    name: Performance regression check
+    runs-on: ubuntu-latest
+    env:
+      CACHE_DIR: ${{ github.workspace }}/.ccache
+      PYTHONPATH: ${{ github.workspace }}/build/tools/mpact/python_packages/mpact    
+    steps:
+    - uses: actions/checkout@v4
+      with:
+          submodules: recursive
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+    - name: Setup Python Version
+      uses: actions/setup-python@v5
+      with:
+        python-version: 3.11 # Install the python version needed
+
+    - name: Set up ccache
+      uses: hendrikmuhs/ccache-action@v1.2
+
+    - name: Install requirements
+      run: |
+           export CCACHE_DIR=${{ env.CACHE_DIR }}
+           python -m pip install --upgrade pip
+           python -m pip install --upgrade pip
+           python -m pip install pytest pytest-benchmark
+           python -m pip install -r externals/torch-mlir/requirements.txt
+           python -m pip install -r externals/torch-mlir/torchvision-requirements.txt
+   
+    - name: Create build directory
+      run: mkdir build
+
+    - name: Configure CMake
+      run: >
+        cmake -GNinja -Bbuild
+        -DCMAKE_BUILD_TYPE=Release
+        -DLLVM_ENABLE_PROJECTS=mlir
+        -DLLVM_ENABLE_ASSERTIONS=ON
+        -DLLVM_EXTERNAL_PROJECTS="torch-mlir;mpact"
+        -DLLVM_EXTERNAL_TORCH_MLIR_SOURCE_DIR="${PWD}/externals/torch-mlir"
+        -DLLVM_EXTERNAL_MPACT_SOURCE_DIR="${PWD}"
+        -DLLVM_TARGETS_TO_BUILD=host
+        -DMLIR_ENABLE_BINDINGS_PYTHON=ON
+        -DCMAKE_C_COMPILER_LAUNCHER=ccache
+        -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+        -DCMAKE_C_COMPILER=clang
+        -DCMAKE_CXX_COMPILER=clang++
+        "externals/torch-mlir/externals/llvm-project/llvm"
+
+    - name: Build
+      run: cmake --build build --target build-benchmark-mpact
+
+    - name: Run benchmark
+      run: pytest benchmark/python/benchmarks/regression_benchmark.py --benchmark-json output.json
+
+    - name: Store benchmark result
+      uses: benchmark-action/github-action-benchmark@v1
+      with:
+        tool: 'pytest'
+        output-file-path: output.json
+        fail-on-alert: true
+        # GitHub API token to make a commit comment
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        # Enable alert commit comment
+        comment-on-alert: true
+        # Mention @yinying-lisa-li in the commit comment
+        alert-comment-cc-users: '@yinying-lisa-li'
+        # Push and deploy GitHub pages branch automatically
+        auto-push: true
+        alert-threshold: 120%
diff --git a/benchmark/python/benchmarks/regression_benchmark.py b/benchmark/python/benchmarks/regression_benchmark.py
new file mode 100644
index 0000000..6b3d0e4
--- /dev/null
+++ b/benchmark/python/benchmarks/regression_benchmark.py
@@ -0,0 +1,63 @@
+import pytest
+from mpact.models.kernels import *
+from mpact_benchmark.utils.tensor_generator import generate_tensor
+
+SHAPE = (1024, 1024)
+SPARSITY = 0.8
+
+dense_tensor1 = generate_tensor(0, SHAPE, SPARSITY)
+dense_tensor2 = generate_tensor(1, SHAPE, SPARSITY)
+dense_tensor3 = generate_tensor(2, SHAPE, SPARSITY)
+dense_vector = generate_tensor(1, (SHAPE[0],), SPARSITY)
+
+sparse_tensor1 = dense_tensor1.to_sparse_csr()
+sparse_tensor2 = dense_tensor2.to_sparse_csr()
+sparse_tensor3 = dense_tensor3.to_sparse_csr()
+
+
+def test_mv_dense(benchmark):
+    benchmark(MVNet(), dense_tensor1, dense_vector)
+
+
+def test_mm_dense(benchmark):
+    benchmark(MMNet(), dense_tensor1, dense_tensor2)
+
+
+def test_add_dense(benchmark):
+    benchmark(AddNet(), dense_tensor1, dense_tensor2)
+
+
+def test_mul_dense(benchmark):
+    benchmark(MulNet(), dense_tensor1, dense_tensor2)
+
+
+def test_nop_dense(benchmark):
+    benchmark(SelfNet(), dense_tensor1)
+
+
+def test_sddmm_dense(benchmark):
+    benchmark(SDDMMNet(), dense_tensor1, dense_tensor2, dense_tensor3)
+
+
+def test_mv_sparse(benchmark):
+    benchmark(MVNet(), sparse_tensor1, dense_vector)
+
+
+def test_mm_sparse(benchmark):
+    benchmark(MMNet(), sparse_tensor1, sparse_tensor2)
+
+
+def test_add_sparse(benchmark):
+    benchmark(AddNet(), sparse_tensor1, sparse_tensor2)
+
+
+def test_mul_sparse(benchmark):
+    benchmark(MulNet(), sparse_tensor1, sparse_tensor2)
+
+
+def test_nop_sparse(benchmark):
+    benchmark(SelfNet(), sparse_tensor1)
+
+
+def test_sddmm_sparse(benchmark):
+    benchmark(SDDMMNet(), sparse_tensor1, dense_tensor2, dense_tensor3)