Skip to content

Commit

Permalink
📊 Cache region/locset during paint/place. (#2389)
Browse files Browse the repository at this point in the history
Add a small (=1 element) cache to avoid constantly calling `thingify` on
the same expressions.
In my experiments, this will save 5-10% time during initialization of
complex cable cells at no extra
cost, as we need to have one item (`mextent` for regions or
`mlocation_list` for locsets) in memory
anyhow. Unfortunately, this means that cable cells are faster or slower
depending on paint/place
_order_, but I don't anticipate _sorting_ decorations first is worth it.

Also, observe our own caching rules to make `busyring` slightly faster.

Anecdoctal performance numbers:
```
busyring:
  Before:   185.95s
  After:    172.94s
```
Tested with 2048 complex cells of depth 10 running on a single core,
pure initialization.
  • Loading branch information
thorstenhater authored Sep 5, 2024
1 parent a4c3fa6 commit b589c88
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 42 deletions.
75 changes: 40 additions & 35 deletions arbor/cable_cell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ struct cable_cell_impl {
dictionary(labels),
decorations(decorations)
{
init(decorations);
init();
}

cable_cell_impl(): cable_cell_impl({},{},{}) {}
Expand All @@ -100,7 +100,7 @@ struct cable_cell_impl {

cable_cell_impl(cable_cell_impl&& other) = default;

void init(const decor&);
void init();

template <typename T>
mlocation_map<T>& get_location_map(const T&) {
Expand All @@ -116,13 +116,13 @@ struct cable_cell_impl {
}

template <typename Item>
void place(const locset& ls, const Item& item, const hash_type& label) {
void place(const mlocation_list& locs, const Item& item, const hash_type& label) {
auto& mm = get_location_map(item);
cell_lid_type& lid = placed_count.get<Item>();
cell_lid_type first = lid;

for (auto l: thingify(ls, provider)) {
placed<Item> p{l, lid++, item};
for (const auto& loc: locs) {
placed<Item> p{loc, lid++, item};
mm.push_back(p);
}
auto range = lid_range(first, lid);
Expand Down Expand Up @@ -160,42 +160,36 @@ struct cable_cell_impl {
return region_map.get<init_reversal_potential>()[init.ion];
}

void paint(const region& reg, const density& prop) {
this->paint(reg, scaled_mechanism<density>(prop));
void paint(const mextent& cables, const std::string& str, const density& prop) {
this->paint(cables, str, scaled_mechanism<density>(prop));
}

void paint(const region& reg, const scaled_mechanism<density>& prop) {
void paint(const mextent& cables, const std::string& str, const scaled_mechanism<density>& prop) {
std::unordered_map<std::string, iexpr_ptr> im;
for (const auto& [fst, snd]: prop.scale_expr) {
im.insert_or_assign(fst, thingify(snd, provider));
for (const auto& [label, iex]: prop.scale_expr) {
im.insert_or_assign(label, thingify(iex, provider));
}

auto& mm = get_region_map(prop.t_mech);
const auto& cables = thingify(reg, provider);
for (const auto& c: cables) {
for (const auto& cable: cables) {
// Skip zero-length cables in extent:
if (c.prox_pos == c.dist_pos) continue;

if (!mm.insert(c, {prop.t_mech, im})) {
std::stringstream rg; rg << reg;
if (cable.prox_pos == cable.dist_pos) continue;
if (!mm.insert(cable, {prop.t_mech, im})) {
throw cable_cell_error(util::pprintf("Setting mechanism '{}' on region '{}' overpaints at cable {}",
prop.t_mech.mech.name(), rg.str(), c));
prop.t_mech.mech.name(), str, cable));
}
}
}

template <typename TaggedMech>
void paint(const region& reg, const TaggedMech& prop) {
mextent cables = thingify(reg, provider);
void paint(const mextent& cables, const std::string& str, const TaggedMech& prop) {
auto& mm = get_region_map(prop);

for (auto c: cables) {
for (const auto& cable: cables) {
// Skip zero-length cables in extent:
if (c.prox_pos==c.dist_pos) continue;

if (!mm.insert(c, prop)) {
std::stringstream rg; rg << reg;
throw cable_cell_error(util::pprintf("Setting property '{}' on region '{}' overpaints at '{}'", show(prop), rg.str(), c));
if (cable.prox_pos == cable.dist_pos) continue;
if (!mm.insert(cable, prop)) {
throw cable_cell_error(util::pprintf("Setting property '{}' on region '{}' overpaints at cable {}",
show(prop), str, cable));
}
}
}
Expand All @@ -214,16 +208,27 @@ impl_ptr make_impl(cable_cell_impl* c) {
return impl_ptr(c, [](cable_cell_impl* p){delete p;});
}

void cable_cell_impl::init(const decor& d) {
for (const auto& p: d.paintings()) {
auto& where = p.first;
std::visit([this, &where] (auto&& what) {this->paint(where, what);}, p.second);
void cable_cell_impl::init() {
// Try to cache with a lookback of one since most models paint/place one
// region/locset in direct succession. We also key on the stringy view of
// expressions since in general equality is undecidable.
std::string last_label = "";
mextent last_region;
mlocation_list last_locset;
for (const auto& [where, what]: decorations.paintings()) {
if (auto region = util::to_string(where); last_label != region) {
last_label = std::move(region);
last_region = thingify(where, provider);
}
std::visit([this, &last_region, &last_label] (auto&& what) { this->paint(last_region, last_label, what); }, what);
}
for (const auto& p: d.placements()) {
auto& where = std::get<0>(p);
auto& label = std::get<2>(p);
std::visit([this, &where, &label] (auto&& what) {return this->place(where, what, label); },
std::get<1>(p));
for (const auto& [where, what, label]: decorations.placements()) {
if (auto locset = util::to_string(where); last_label != locset) {
last_label = std::move(locset);
last_locset = thingify(where, provider);
}
std::visit([this, &last_locset, &label=label] (auto&& what) { return this->place(last_locset, what, label); },
what);
}
}

Expand Down
12 changes: 5 additions & 7 deletions example/busyring/ring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,6 @@ arb::cable_cell complex_cell(arb::cell_gid_type gid, const cell_parameters& para
decor.paint(soma, arb::axial_resistivity{133.577*U::Ohm*U::cm});
decor.paint(soma, arb::membrane_capacitance{4.21567e-2*U::F/U::m2});

decor.paint(dend, arb::axial_resistivity{68.355*U::Ohm*U::cm});
decor.paint(dend, arb::membrane_capacitance{2.11248e-2*U::F/U::m2});

decor.paint(soma, arb::density("pas/e=-76.4024", {{"g", 0.000119174}}));
decor.paint(soma, arb::density("NaV", {{"gbar", 0.0499779}}));
decor.paint(soma, arb::density("SK", {{"gbar", 0.000733676}}));
Expand All @@ -410,18 +407,19 @@ arb::cable_cell complex_cell(arb::cell_gid_type gid, const cell_parameters& para
decor.paint(soma, arb::density("CaDynamics", {{"gamma", 0.0177038}, {"decay", 42.2507}}));
decor.paint(soma, arb::density("Ih", {{"gbar", 1.07608e-07}}));

decor.paint(dend, arb::axial_resistivity{68.355*U::Ohm*U::cm});
decor.paint(dend, arb::membrane_capacitance{2.11248e-2*U::F/U::m2});

decor.paint(dend, arb::density("pas/e=-88.2554", {{"g", 9.57001e-05}}));
decor.paint(dend, arb::density("NaV", {{"gbar", 0.0472215}}));
decor.paint(dend, arb::density("Kv3_1", {{"gbar", 0.186859}}));
decor.paint(dend, arb::density("Im_v2", {{"gbar", 0.00132163}}));
decor.paint(dend, arb::density("Ih", {{"gbar", 9.18815e-06}}));

decor.place(cntr, arb::threshold_detector{-20.0*U::mV}, "d");
decor.place(cntr, arb::synapse("expsyn"), "p");
if (params.synapses>1) {
decor.place(syns, arb::synapse("expsyn"), "s");
}

decor.place(cntr, arb::threshold_detector{-20.0*U::mV}, "d");
if (params.synapses>1) decor.place(syns, arb::synapse("expsyn"), "s");

decor.set_default(arb::cv_policy_every_segment());

Expand Down

0 comments on commit b589c88

Please sign in to comment.