diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f739d8f125b2c97c87d9147b9689a33d321ea52d
--- /dev/null
+++ b/.github/workflows/benchmarks.yml
@@ -0,0 +1,40 @@
+name: Benchmarks
+
+on:
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+    name: "Benchmarks"
+    runs-on: ubuntu-22.04
+    strategy:
+      fail-fast: false
+    env:
+        CC:  gcc-11
+        CXX: g++-11
+    steps:
+      - name: Set up cmake
+        uses: jwlawson/actions-setup-cmake@v1.13
+        with:
+          cmake-version: 3.22.x
+      - name: Clone w/ submodules
+        uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      - name: Build arbor
+        run: |
+          mkdir build
+          cd build
+          cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_MPI=OFF -DARB_USE_BUNDLED_LIBS=ON
+          make -j4 ubenches
+          cd -
+      - name: Run benchmarks
+        run: |
+          build/bin/accumulate_functor_values
+          build/bin/default_construct
+          build/bin/event_setup
+          build/bin/event_binning
+          build/bin/fvm_discretize
+          build/bin/mech_vec
+          build/bin/task_system
diff --git a/test/ubench/fvm_discretize.cpp b/test/ubench/fvm_discretize.cpp
index 124e144ed98167f2b98712efd51cde02c04f1f69..7d0c6bb836ea5cdc2fa198edf2533c56bf3ba210 100644
--- a/test/ubench/fvm_discretize.cpp
+++ b/test/ubench/fvm_discretize.cpp
@@ -33,7 +33,7 @@ void run_cv_geom(benchmark::State& state) {
     auto gdflt = neuron_parameter_defaults;
     const std::size_t ncv_per_branch = state.range(0);
 
-    cable_cell c(from_swc(SWCFILE));
+    cable_cell c(from_swc(SWCFILE), {});
     auto ends = cv_policy_fixed_per_branch(ncv_per_branch).cv_boundary_points(c);
 
     while (state.KeepRunning()) {
@@ -44,7 +44,7 @@ void run_cv_geom(benchmark::State& state) {
 void run_cv_geom_every_segment(benchmark::State& state) {
     auto gdflt = neuron_parameter_defaults;
 
-    cable_cell c(from_swc(SWCFILE));
+    cable_cell c(from_swc(SWCFILE), {});
     auto ends = cv_policy_every_segment().cv_boundary_points(c);
 
     while (state.KeepRunning()) {
@@ -55,7 +55,7 @@ void run_cv_geom_every_segment(benchmark::State& state) {
 void run_cv_geom_explicit(benchmark::State& state) {
     auto gdflt = neuron_parameter_defaults;
 
-    cable_cell c(from_swc(SWCFILE));
+    cable_cell c(from_swc(SWCFILE), {});
 
     while (state.KeepRunning()) {
         auto ends = cv_policy_every_segment().cv_boundary_points(c);
@@ -74,7 +74,7 @@ void run_discretize(benchmark::State& state) {
     dec.set_default(cv_policy_fixed_per_branch(ncv_per_branch));
 
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, {}, dec}, gdflt));
+        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, dec}, gdflt));
     }
 }
 
@@ -86,7 +86,7 @@ void run_discretize_every_segment(benchmark::State& state) {
     dec.set_default(cv_policy_every_segment());
 
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, {}, dec}, gdflt));
+        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, dec}, gdflt));
     }
 }
 
@@ -95,11 +95,11 @@ void run_discretize_explicit(benchmark::State& state) {
 
     decor dec;
     auto morpho = from_swc(SWCFILE);
-    auto ends = cv_policy_every_segment().cv_boundary_points(cable_cell{morpho});
+    auto ends = cv_policy_every_segment().cv_boundary_points(cable_cell{morpho, {}});
     dec.set_default(cv_policy_explicit(std::move(ends)));
 
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, {}, dec}, gdflt));
+        benchmark::DoNotOptimize(fvm_cv_discretize(cable_cell{morpho, dec}, gdflt));
     }
 }
 
diff --git a/test/ubench/mech_vec.cpp b/test/ubench/mech_vec.cpp
index 5da4a6c021ae0af6a943482644b18b3923e4c9b3..880774fda916f2df3c7ef7608a2816e6b6835b5a 100644
--- a/test/ubench/mech_vec.cpp
+++ b/test/ubench/mech_vec.cpp
@@ -71,7 +71,7 @@ public:
             decor.place(arb::mlocation{0, distribution(gen)}, arb::synapse("expsyn"), "syn");
         }
 
-        return arb::cable_cell{arb::morphology(tree), {}, decor};
+        return arb::cable_cell{arb::morphology(tree), decor};
     }
 
     virtual cell_kind get_cell_kind(cell_gid_type) const override {
@@ -112,7 +112,7 @@ public:
         decor.paint(arb::reg::all(), arb::density("pas"));
         decor.set_default(arb::cv_policy_max_extent((dend_length+soma_radius*2)/num_comp_));
 
-        return arb::cable_cell {arb::morphology(tree), {}, decor};
+        return arb::cable_cell {arb::morphology(tree), decor};
     }
 
     virtual cell_kind get_cell_kind(cell_gid_type) const override {
@@ -155,7 +155,7 @@ public:
         decor.paint(arb::reg::all(), arb::density("pas"));
         decor.set_default(arb::cv_policy_max_extent((dend_length*3+soma_radius*2)/num_comp_));
 
-        return arb::cable_cell{arb::morphology(tree), {}, decor};
+        return arb::cable_cell{arb::morphology(tree), decor};
     }
 
     virtual cell_kind get_cell_kind(cell_gid_type) const override {
@@ -196,7 +196,7 @@ public:
         decor.paint(arb::reg::all(), arb::density("hh"));
         decor.set_default(arb::cv_policy_max_extent((dend_length+soma_radius*2)/num_comp_));
 
-        return arb::cable_cell{arb::morphology(tree), {}, decor};
+        return arb::cable_cell{arb::morphology(tree), decor};
     }
 
     virtual cell_kind get_cell_kind(cell_gid_type) const override {
@@ -239,7 +239,7 @@ public:
         decor.paint(arb::reg::all(), arb::density("hh"));
         decor.set_default(arb::cv_policy_max_extent((dend_length*3+soma_radius*2)/num_comp_));
 
-        return arb::cable_cell{arb::morphology(tree), {}, decor};
+        return arb::cable_cell{arb::morphology(tree), decor};
     }
 
     virtual cell_kind get_cell_kind(cell_gid_type) const override {