diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index 1d5415c7d9297d3c134b3910c63489e90e3b5886..ffc86d5e711885b4f1c3e75245a7594486536627 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -12,6 +12,7 @@
 #include <arbor/util/optional.hpp>
 
 #include "fvm_layout.hpp"
+#include "threading/threading.hpp"
 #include "util/maputil.hpp"
 #include "util/meta.hpp"
 #include "util/partition.hpp"
@@ -446,12 +447,16 @@ fvm_cv_discretization fvm_cv_discretize(const cable_cell& cell, const cable_cell
 }
 
 fvm_cv_discretization fvm_cv_discretize(const std::vector<cable_cell>& cells,
-    const cable_cell_parameter_set& global_defaults)
+    const cable_cell_parameter_set& global_defaults,
+    const arb::execution_context& ctx)
 {
-    fvm_cv_discretization combined;
+    std::vector<fvm_cv_discretization> cell_disc(cells.size());
+    threading::parallel_for::apply(0, cells.size(), ctx.thread_pool.get(),
+          [&] (int i) { cell_disc[i]=fvm_cv_discretize(cells[i], global_defaults);});
 
-    for (const auto& c: cells) {
-        append(combined, fvm_cv_discretize(c, global_defaults));
+    fvm_cv_discretization combined;
+    for (auto cell_idx: count_along(cells)) {
+        append(combined, cell_disc[cell_idx]);
     }
     return combined;
 }
@@ -511,11 +516,15 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
     const cable_cell& cell, const fvm_cv_discretization& D, fvm_size_type cell_idx);
 
 fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& gprop,
-    const std::vector<cable_cell>& cells, const fvm_cv_discretization& D)
+    const std::vector<cable_cell>& cells, const fvm_cv_discretization& D, const execution_context& ctx)
 {
+    std::vector<fvm_mechanism_data> cell_mech(cells.size());
+    threading::parallel_for::apply(0, cells.size(), ctx.thread_pool.get(),
+          [&] (int i) { cell_mech[i]=fvm_build_mechanism_data(gprop, cells[i], D, i);});
+
     fvm_mechanism_data combined;
     for (auto cell_idx: count_along(cells)) {
-        append(combined, fvm_build_mechanism_data(gprop, cells[cell_idx], D, cell_idx));
+        append(combined, cell_mech[cell_idx]);
     }
     return combined;
 }
diff --git a/arbor/fvm_layout.hpp b/arbor/fvm_layout.hpp
index 3e634cd372f107c76562f79be62ae3b96c3655d9..1c5566f81482c94f8941ed2f5bf49f7d3deaa418 100644
--- a/arbor/fvm_layout.hpp
+++ b/arbor/fvm_layout.hpp
@@ -11,6 +11,7 @@
 #include <arbor/mechcat.hpp>
 #include <arbor/recipe.hpp>
 
+#include "execution_context.hpp"
 #include "util/piecewise.hpp"
 #include "util/rangeutil.hpp"
 #include "util/span.hpp"
@@ -112,7 +113,7 @@ fvm_cv_discretization& append(fvm_cv_discretization&, const fvm_cv_discretizatio
 // Construct fvm_cv_discretization from one or more cells.
 
 fvm_cv_discretization fvm_cv_discretize(const cable_cell& cell, const cable_cell_parameter_set& global_dflt);
-fvm_cv_discretization fvm_cv_discretize(const std::vector<cable_cell>& cells, const cable_cell_parameter_set& global_defaults);
+fvm_cv_discretization fvm_cv_discretize(const std::vector<cable_cell>& cells, const cable_cell_parameter_set& global_defaults, const arb::execution_context& ctx={});
 
 // Post-discretization data for point and density mechanism instantiation.
 
@@ -172,6 +173,6 @@ struct fvm_mechanism_data {
     std::size_t n_target = 0;
 };
 
-fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& gprop, const std::vector<cable_cell>& cells, const fvm_cv_discretization& D);
+fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties& gprop, const std::vector<cable_cell>& cells, const fvm_cv_discretization& D, const arb::execution_context& ctx={});
 
 } // namespace arb
diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp
index 15580a16633350c052ebb972c411a53b5fb95d8c..23ecd5035d56caeea5f8c8900959f8ef7456047e 100644
--- a/arbor/fvm_lowered_cell_impl.hpp
+++ b/arbor/fvm_lowered_cell_impl.hpp
@@ -356,15 +356,17 @@ void fvm_lowered_cell_impl<B>::initialize(
     std::vector<cable_cell> cells;
     const std::size_t ncell = gids.size();
 
-    cells.reserve(ncell);
-    for (auto gid: gids) {
-        try {
-            cells.push_back(any_cast<cable_cell>(rec.get_cell_description(gid)));
-        }
-        catch (util::bad_any_cast&) {
-            throw bad_cell_description(rec.get_cell_kind(gid), gid);
-        }
-    }
+    cells.resize(ncell);
+    threading::parallel_for::apply(0, gids.size(), context_.thread_pool.get(),
+           [&](cell_size_type i) {
+               auto gid = gids[i];
+               try {
+                   cells[i] = any_cast<cable_cell&&>(rec.get_cell_description(gid));
+               }
+               catch (util::bad_any_cast&) {
+                   throw bad_cell_description(rec.get_cell_kind(gid), gid);
+               }
+           });
 
     cable_cell_global_properties global_props;
     try {
@@ -397,7 +399,7 @@ void fvm_lowered_cell_impl<B>::initialize(
 
     // Discretize cells, build matrix.
 
-    fvm_cv_discretization D = fvm_cv_discretize(cells, global_props.default_parameters);
+    fvm_cv_discretization D = fvm_cv_discretize(cells, global_props.default_parameters, context_);
 
     std::vector<index_type> cv_to_intdom(D.size());
     std::transform(D.geometry.cv_to_cell.begin(), D.geometry.cv_to_cell.end(), cv_to_intdom.begin(),
@@ -410,7 +412,7 @@ void fvm_lowered_cell_impl<B>::initialize(
 
     // Discretize mechanism data.
 
-    fvm_mechanism_data mech_data = fvm_build_mechanism_data(global_props,  cells, D);
+    fvm_mechanism_data mech_data = fvm_build_mechanism_data(global_props, cells, D, context_);
 
     // Discretize and build gap junction info.
 
diff --git a/arbor/include/arbor/cable_cell.hpp b/arbor/include/arbor/cable_cell.hpp
index f8b62eb1e545c89222efa2f2ec1f131889588666..fd876e1e2182f406f2057588fda10d6a26677128 100644
--- a/arbor/include/arbor/cable_cell.hpp
+++ b/arbor/include/arbor/cable_cell.hpp
@@ -99,6 +99,8 @@ public:
     /// Move constructor
     cable_cell(cable_cell&& other) = default;
 
+    cable_cell& operator=(cable_cell&&) = default;
+
     /// construct from morphology
     cable_cell(const class morphology& m, const label_dict& dictionary={});
 
diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp
index 5f7b116ebe0fa9570832583d0ed7a698e4007778..c6b15e562aac01c5cd41814e902c7471fd46329b 100644
--- a/test/unit/test_fvm_lowered.cpp
+++ b/test/unit/test_fvm_lowered.cpp
@@ -334,7 +334,7 @@ TEST(fvm_lowered, stimulus) {
     cable_cell_global_properties gprop;
     gprop.default_parameters = neuron_parameter_defaults;
 
-    fvm_cv_discretization D = fvm_cv_discretize(cells, gprop.default_parameters);
+    fvm_cv_discretization D = fvm_cv_discretize(cells, gprop.default_parameters, context);
     const auto& A = D.cv_area;
 
     std::vector<target_handle> targets;
@@ -837,7 +837,7 @@ TEST(fvm_lowered, gj_coords_simple) {
         cells.push_back(std::move(c));
     }
 
-    fvm_cv_discretization D = fvm_cv_discretize(cells, neuron_parameter_defaults);
+    fvm_cv_discretization D = fvm_cv_discretize(cells, neuron_parameter_defaults, context);
 
     std::vector<cell_gid_type> gids = {0, 1};
     auto GJ = fvcell.fvm_gap_junctions(cells, gids, rec, D);
@@ -943,7 +943,7 @@ TEST(fvm_lowered, gj_coords_complex) {
 
     gap_recipe rec;
     fvcell.fvm_intdom(rec, gids, cell_to_intdom);
-    fvm_cv_discretization D = fvm_cv_discretize(cells, neuron_parameter_defaults);
+    fvm_cv_discretization D = fvm_cv_discretize(cells, neuron_parameter_defaults, context);
 
     int c0_gj_cv[2];
     for (int i = 0; i<2; ++i) c0_gj_cv[i] = D.geometry.location_cv(0, c0_gj[i]);
@@ -1043,8 +1043,8 @@ TEST(fvm_lowered, cell_group_gj) {
     auto num_dom0 = fvcell.fvm_intdom(rec, gids_cg0, cell_to_intdom0);
     auto num_dom1 = fvcell.fvm_intdom(rec, gids_cg1, cell_to_intdom1);
 
-    fvm_cv_discretization D0 = fvm_cv_discretize(cell_group0, neuron_parameter_defaults);
-    fvm_cv_discretization D1 = fvm_cv_discretize(cell_group1, neuron_parameter_defaults);
+    fvm_cv_discretization D0 = fvm_cv_discretize(cell_group0, neuron_parameter_defaults, context);
+    fvm_cv_discretization D1 = fvm_cv_discretize(cell_group1, neuron_parameter_defaults, context);
 
     auto GJ0 = fvcell.fvm_gap_junctions(cell_group0, gids_cg0, rec, D0);
     auto GJ1 = fvcell.fvm_gap_junctions(cell_group1, gids_cg1, rec, D1);