From 27ff416327c905a17fcdda3baf4f63a06c198089 Mon Sep 17 00:00:00 2001
From: Nora Abi Akar <nora.abiakar@gmail.com>
Date: Fri, 27 Mar 2020 11:50:15 +0100
Subject: [PATCH] optimize model-init runtime (#985)
`model-init` run-times have been observed to be too high when running systems with many synapses.
The main culprit is the `sort_by` function (fvm_layout:723). When testing on a model with 1024 cells and 10000 synapses, 65% of model-init is spent in this function.
Comparing two `std::map<std::string, double>` is really expensive. We can replace the string map `synapse_instance.param_value` with a sorted `std::vector<std::pair<unsigned, double>>`, and have an additional `std::map<std::string, unsigned> param_map` to keep track of all the param_name -> unsigned mappings.
`model-init` run-time is 2.5x faster (tested on my 4 core i7-7600U laptop):
```
10000 synapses/cell
--------------------------------------------------------------------
| num cells | model-init before (s) | model-init after (s) |
--------------------------------------------------------------------
| 1024 | 12 | 5 |
| 2048 | 25.5 | 10.8 |
| 4096 | 55.5 | 23.2 |
| 8192 | 121.8 | 49 |
--------------------------------------------------------------------
```
---
arbor/fvm_layout.cpp | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index ffc86d5e..19a68d2e 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -684,29 +684,38 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
struct synapse_instance {
size_type cv;
- std::map<std::string, double> param_value; // uses ordering of std::map
+ std::vector<std::pair<unsigned, double>> param_value; // sorted vector of <param_id,value> pairs
size_type target_index;
};
for (const auto& entry: cell.synapses()) {
const std::string& name = entry.first;
mechanism_info info = catalogue[name];
- std::map<std::string, double> default_param_value;
std::vector<synapse_instance> sl;
+ // Map from a param string identifier to a unique unsigned int identifier
+ std::map<std::string, unsigned> param_map;
+
+ // Map from a param unsigned int identifier to a default value
+ std::map<unsigned, double> default_param_value;
+
+ unsigned id=0;
for (const auto& kv: info.parameters) {
- default_param_value[kv.first] = kv.second.default_value;
+ param_map[kv.first] = id;
+ default_param_value[id++] = kv.second.default_value;
}
for (const placed<mechanism_desc>& pm: entry.second) {
verify_mechanism(info, pm.item);
synapse_instance in;
- in.param_value = default_param_value;
+ auto param_value_map = default_param_value;
for (const auto& kv: pm.item.values()) {
- in.param_value.at(kv.first) = kv.second;
+ param_value_map.at(param_map.at(kv.first)) = kv.second;
}
+ std::copy(param_value_map.begin(), param_value_map.end(), std::back_inserter(in.param_value));
+ std::sort(in.param_value.begin(), in.param_value.end());
in.target_index = pm.lid;
in.cv = D.geometry.location_cv(cell_idx, pm.loc);
@@ -728,7 +737,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
fvm_mechanism_config config;
config.kind = mechanismKind::point;
- for (auto& pentry: default_param_value) {
+ for (auto& pentry: param_map) {
config.param_values.emplace_back(pentry.first, std::vector<value_type>{});
}
@@ -746,9 +755,12 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
}
unsigned j = 0;
- for (auto& pentry: in.param_value) {
+ for (auto& pentry: param_map) {
arb_assert(config.param_values[j].first==pentry.first);
- config.param_values[j++].second.push_back(pentry.second);
+ auto it = std::lower_bound(in.param_value.begin(), in.param_value.end(), pentry.second, [](auto el, unsigned val) {return el.first < val;});
+
+ arb_assert(it!= in.param_value.end());
+ config.param_values[j++].second.push_back((*it).second);
}
}
config.target.push_back(in.target_index);
--
GitLab