-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create less intermediate data from events (#2249)
This PR optimizes event handling by removing the intermediate structure created by this chain of transformations: 1. `event_lanes := vector<vector<event>>` (one lane per local cell, each lane sorted by time) 2. `staged_events_per_mech_id := vector<vector<vector<deliverable_event>>>` (one vector per mech id and time step, sorted by time) 3. `vector<event_stream>` one stream per mech id The following optimisations where performed: - cut out the middle step (2) completely as it is wholly unneeded and sort directly into event streams - remove a spurious index structure from `cable_cell_group` - slim down `deliverable_event` and `deliverable_event_data` - `event_stream` now uses a partition instead of a vector of ranges for splitting its data into `dt` buckets. (Save 8B per `dt` ;)) The result is that the quite pathological example `calcium_stdp.py` (as given in which generates immense amounts of spikes using a single cell group and a single epoch drops from 3.8GB heap to 1.9GB heap usage at peak using the same runtime or slightly less (<5% difference). ## TODO - [x] Port to GPU. - [X] Tests pass - [x] Examples run through - [x] Fix tests... Interestingly _locally_ all tests pass on my dev machine regardless of optimisation, vectorisation, and assertion settings. --------- Co-authored-by: boeschf <[email protected]>
- Loading branch information
1 parent
e6b78db
commit 047550d
Showing
22 changed files
with
301 additions
and
327 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,107 @@ | ||
#pragma once | ||
|
||
#include <type_traits> | ||
#include <vector> | ||
|
||
#include <arbor/generic_event.hpp> | ||
#include <arbor/mechanism_abi.h> | ||
|
||
|
||
#include "backends/event.hpp" | ||
#include "backends/event_stream_state.hpp" | ||
#include "event_lane.hpp" | ||
#include "timestep_range.hpp" | ||
#include "util/partition.hpp" | ||
|
||
ARB_SERDES_ENABLE_EXT(arb_deliverable_event_data, mech_index, weight); | ||
|
||
namespace arb { | ||
|
||
template <typename Event, typename Span> | ||
class event_stream_base { | ||
public: // member types | ||
template <typename Event> | ||
struct event_stream_base { | ||
using size_type = std::size_t; | ||
using event_type = Event; | ||
using event_time_type = ::arb::event_time_type<Event>; | ||
using event_data_type = ::arb::event_data_type<Event>; | ||
|
||
protected: // private member types | ||
using span_type = Span; | ||
|
||
static_assert(std::is_same<decltype(std::declval<span_type>().begin()), event_data_type*>::value); | ||
static_assert(std::is_same<decltype(std::declval<span_type>().end()), event_data_type*>::value); | ||
|
||
protected: // members | ||
std::vector<event_data_type> ev_data_; | ||
std::vector<span_type> ev_spans_; | ||
std::vector<std::size_t> ev_spans_ = {0}; | ||
size_type index_ = 0; | ||
event_data_type* base_ptr_ = nullptr; | ||
|
||
public: | ||
event_stream_base() = default; | ||
|
||
// returns true if the currently marked time step has no events | ||
bool empty() const { | ||
return ev_spans_.empty() || ev_data_.empty() || !index_ || index_ > ev_spans_.size() || | ||
!ev_spans_[index_-1].size(); | ||
return ev_data_.empty() // No events | ||
|| index_ < 1 // Since we index with a left bias, index_ must be at least 1 | ||
|| index_ >= ev_spans_.size() // Cannot index at container length | ||
|| ev_spans_[index_-1] >= ev_spans_[index_]; // Current span is empty | ||
} | ||
|
||
void mark() { | ||
index_ += (index_ <= ev_spans_.size() ? 1 : 0); | ||
} | ||
void mark() { index_ += 1; } | ||
|
||
auto marked_events() { | ||
using std::begin; | ||
using std::end; | ||
if (empty()) { | ||
return make_event_stream_state((event_data_type*)nullptr, (event_data_type*)nullptr); | ||
} else { | ||
return make_event_stream_state(begin(ev_spans_[index_-1]), end(ev_spans_[index_-1])); | ||
auto beg = (event_data_type*)nullptr; | ||
auto end = (event_data_type*)nullptr; | ||
if (!empty()) { | ||
beg = base_ptr_ + ev_spans_[index_-1]; | ||
end = base_ptr_ + ev_spans_[index_]; | ||
} | ||
return make_event_stream_state(beg, end); | ||
} | ||
|
||
// clear all previous data | ||
void clear() { | ||
ev_data_.clear(); | ||
// Clear + push doesn't allocate a new vector | ||
ev_spans_.clear(); | ||
ev_spans_.push_back(0); | ||
base_ptr_ = nullptr; | ||
index_ = 0; | ||
} | ||
|
||
// Construct a mapping of mech_id to a stream s.t. streams are partitioned into | ||
// time step buckets by `ev_span` | ||
template<typename EventStream> | ||
static std::enable_if_t<std::is_base_of_v<event_stream_base, EventStream>> | ||
multi_event_stream(const event_lane_subrange& lanes, | ||
const std::vector<target_handle>& handles, | ||
const std::vector<std::size_t>& divs, | ||
const timestep_range& steps, | ||
std::unordered_map<unsigned, EventStream>& streams) { | ||
auto n_steps = steps.size(); | ||
|
||
std::unordered_map<unsigned, std::vector<std::size_t>> dt_sizes; | ||
for (auto& [k, v]: streams) { | ||
v.clear(); | ||
dt_sizes[k].resize(n_steps, 0); | ||
} | ||
|
||
auto cell = 0; | ||
for (auto& lane: lanes) { | ||
auto div = divs[cell]; | ||
arb_size_type step = 0; | ||
for (auto evt: lane) { | ||
auto time = evt.time; | ||
auto weight = evt.weight; | ||
auto target = evt.target; | ||
while(step < n_steps && time >= steps[step].t_end()) ++step; | ||
// Events coinciding with epoch's upper boundary belong to next epoch | ||
if (step >= n_steps) break; | ||
auto& handle = handles[div + target]; | ||
streams[handle.mech_id].ev_data_.push_back({handle.mech_index, weight}); | ||
dt_sizes[handle.mech_id][step]++; | ||
} | ||
++cell; | ||
} | ||
|
||
for (auto& [id, stream]: streams) { | ||
util::make_partition(stream.ev_spans_, dt_sizes[id]); | ||
stream.init(); | ||
} | ||
} | ||
}; | ||
|
||
} // namespace arb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.