Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Mechanism ABI #1376

Closed
2 of 3 tasks
thorstenhater opened this issue Feb 15, 2021 · 0 comments · Fixed by #1452
Closed
2 of 3 tasks

Add Mechanism ABI #1376

thorstenhater opened this issue Feb 15, 2021 · 0 comments · Fixed by #1452
Assignees
Milestone

Comments

@thorstenhater
Copy link
Contributor

thorstenhater commented Feb 15, 2021

Introduction

Add an extension interface to Arbor in plain C, so that implementors of mechanisms do not need to have access to the internals of Arbor. This pulls in a lot of changes, some of which are in implementation, some still in the planning phase.

The Interface

Here is my current suggestion of how this interface should look like

#ifndef ARB_MECH_ABI
#define ARB_MECH_ABI

// FVM typedefs
typedef double value_type;
typedef size_t index_type;
typedef size_t size_type;
typedef size_t lid_type;      // TODO This needs to be synced with the status quo (uint32_t)

// Selectors
enum mechanism_kind { point, density, reversal_potential };
enum backend_kind { cpu, gpu };

// Ion state variables; view into shared_state
typedef struct {
    value_type* current_density;
    value_type* reversal_potential;
    value_type* internal_concentration;
    value_type* external_concentration;
    value_type* ionic_charge;
} ion_state;

// Event; consumed by `apply_event`
typedef struct {
    value_type  time;
    value_type  weight;
    lid_type    mech_id;       // mechanism type identifier (per cell group).
    lid_type    mech_index;    // instance of the mechanism
    lid_type    intdom_index;  // which integration domain (acts as index into e.g. vec_t)
} deliverable_event;

// A set of n streams of events, where those in the ranges (events + begin, events + end) are meant to be consumed
typedef struct {
    size_type          n;      // number of streams
    deliverable_event* events; // array of event data items
    index_type*        begin;  // array of offsets to beginning of marked events
    index_type*        end;    // array of offsets to end of marked events
}  deliverable_event_stream;

// Parameter Pack
typedef struct {
    // Base type
    index_type width;
    index_type n_detectors;
    index_type* vec_ci;
    index_type* vec_di;
    value_type* vec_t;
    value_type* vec_dt;
    value_type* vec_v;
    value_type* vec_i;
    value_type* vec_g;
    value_type* temperature_degC;
    value_type* diam_um;
    value_type* time_since_spike;
    index_type* node_index;
    index_type* multiplicity;
    value_type* weight;

    // Event delivery
    deliverable_event_stream events;

    // Extensible part
    value_type* constants;
    value_type* fields;
    value_type* defaults;
    value_type* globals;
    ion_state*  ion_states;
    index_type* ion_indices;
} ppack;

// Actual ABI struct
typedef void (*abi_method)(ppack*);

typedef struct {
    // Metadata
    unsigned long  abi_version;     // ABI version used to build this mechanism
    char*          fingerprint;     // Free form field; provide a unique ID
    mechanism_kind kind;            // Point, Density, ReversalPotential, ...
    backend_kind   backend;         // GPU, CPU, ...
    size_type      partition_width; // Width for partitioning indices, based on SIMD.
    size_type      chunk_size;   // Stride between arrays 

    // Tables
    const char** globals;           // Global values
    const char** ions;              // Ion properties and indices
    const char** fields;            // Mechanism fields and defaults
    const char** state_vars;        // Integrable state

    // Interface methods
    abi_method advance_state;       // Integrate state variables
    abi_method post_event;          // Process post synaptic events
    abi_method compute_currents;    // Compute ionic currents
    abi_method apply_events;        // Apply events to state
    abi_method write_ions;          // Add ion concentrations to shared state
    abi_method init_mechanism;      // INITIAL block
} abi_type;

#endif

Changes to Arbor

In the following, I will discuss how this ties into Arbor and what changes from the status quo.

The classes arb::backend::mechanism wrap abi_type and call the relevant interface methods where needed, passing ppack as needed. This quite similar to how it is done now, with some exceptions

  • ppack is no longer extended through inheritance.
  • all backends use ppack, not only the gpu kind.
  • no state hidden in ppack allowed.
  • the _table methods have been replaced by plain string arrays (terminated by a NULL string)

Upon instantiation of a mechanism, the wrapper will allocate two arrays for bulk storage of data and indices, as well as space for the ppack struct. Storage will be aligned according to partition_width (set by mechanism). Then, the tables will be traversed to allocate and set the pointer members of ppack, allofwhich are views into shared_state or the bulk storage arrays. The main difference is here that the mechanism does not return pointers to the members of ppack, just the in-order list of items it wishes to access. This comes at the cost of some boilerplate on the ABI implementer's side

An example might be illustrative here

// Status quo, shortened
class mechanism_cpu_ca_Dynamics: public base {
public:
    // ...    
    mechanism_global_table global_table() override { return {{"F", &F}}; }
    mechanism_field_table  field_table()  override { return {{"depth", &depth}, {"minCai", &minCai}}; }
    mechanism_state_table state_table() override { return {{"cai", &cai}}; }    
private:
    value_type F = 96485.332123310014;
    value_type* depth;
    value_type* minCai;
};

void mechanism_cpu_CaDynamics::init() {
    for (int i_ = 0; i_ < n_; ++i_) {
        cai[i_] = minCai[i_];
    }
}

becomes

// New
const char*[] ca_dyn_globals = {"F", NULL};
const char*[] ca_dyn_fields   = {"depth", "min_cai", NULL};
const char*[] ca_dyn_states = {"cai", NULL};

void mechanism_cpu_ca_dyn_init(ppack* pp) {
    // This is extra boilerplate
    size_t stride = pp->chunk_size;
    value_type* cai         = pp->states + stride*0;
    value_type* min_cai = pp->fields  + stride*1;
    for (int i_ = 0; i_ < n_; ++i_) {
        cai[i_] = min_cai[i_];
    }
}

which would be exposed to the library as

abi_type ca_dyn {
    // ...
    .globals = ca_dyn_globals;
    .fields    = ca_dyn_fields;
    .states   = ca_dyn_states;
    // ...
    .init_mechanism = mechanism_cpu_ca_dyn_init;
};

Status

Clarifications

  • chunk_size and partition_width:
    • Partition width is used by the mechanism to indicate its requirements regarding the bundling of values due to SIMD constraints.
    • chunk_size is set by the library to indicate the stride between two array in the bulk storage and may depend on partition_width
@thorstenhater thorstenhater self-assigned this Feb 15, 2021
halfflat pushed a commit that referenced this issue Jul 29, 2021
Implements #1376.

* Provide a common C linkage ABI for externally compiled mechanisms, for both CPU and GPU.
* Remove mechanism type hierarchy (`concrete_mechanism` etc.), and move corresponding functionality to the back-end shared state objects. Mechanism catalogue is no longer indexed by type id.
* Distinguish between SIMD optimal alignment and SIMD width with new `min_align` attribute. Mechanisms provide both pieces of information via ABI.
max9901 added a commit to max9901/arbor that referenced this issue Aug 5, 2021
* Ci/sanitize (arbor-sim#1521)

Add clang sanitizer passes to GH CI.

* Tutorial structure: remove deduplication, includify (arbor-sim#1575)

* added notes re literal_include to python examples
* merged changes in arbor-sim#1504 
* deduped and includified (arbor-sim#1558) tutorials.
* First divergence of tutorial and code spotted!
* Added some contrib documentation for examples and docs.

* Implement mechanism ABI

Implements arbor-sim#1376.

* Provide a common C linkage ABI for externally compiled mechanisms, for both CPU and GPU.
* Remove mechanism type hierarchy (`concrete_mechanism` etc.), and move corresponding functionality to the back-end shared state objects. Mechanism catalogue is no longer indexed by type id.
* Distinguish between SIMD optimal alignment and SIMD width with new `min_align` attribute. Mechanisms provide both pieces of information via ABI.

* update spack package to include fmt for [email protected]: (arbor-sim#1609)

* alles werkt weer allen smol_dend niet;'

* nu werkt het

* Docs mechabi (arbor-sim#1610)

* Bit of review on the copy of the mech abi docs.
* Convert api/abi docs to C Domain directives.

* Bug/assorted static analysis (arbor-sim#1615)

* Fix failure to return value in `py_mech_cat_value_iterator `.
* String butchered by formatting.
* Join unnecessary separation of string literals in `arborio/cabelio.cpp`
* Add missing throw in s_expr code.
* Return value, not reference, in `merge_iterator::operator++(int)`.
* Check for self-assignment in `mechanism_catalogue`.
* Initialize all members of `mechanism`.
* Fix index check order in `multi_event_stream` debug output.
* Use coordinator_ from base class in `memory::array`.

Co-authored-by: thorstenhater <[email protected]>
Co-authored-by: Brent Huisman <[email protected]>
Co-authored-by: Ben Cumming <[email protected]>
max9901 added a commit to max9901/arbor that referenced this issue Aug 9, 2021
* Ci/sanitize (arbor-sim#1521)

Add clang sanitizer passes to GH CI.

* Tutorial structure: remove deduplication, includify (arbor-sim#1575)

* added notes re literal_include to python examples
* merged changes in arbor-sim#1504 
* deduped and includified (arbor-sim#1558) tutorials.
* First divergence of tutorial and code spotted!
* Added some contrib documentation for examples and docs.

* Implement mechanism ABI

Implements arbor-sim#1376.

* Provide a common C linkage ABI for externally compiled mechanisms, for both CPU and GPU.
* Remove mechanism type hierarchy (`concrete_mechanism` etc.), and move corresponding functionality to the back-end shared state objects. Mechanism catalogue is no longer indexed by type id.
* Distinguish between SIMD optimal alignment and SIMD width with new `min_align` attribute. Mechanisms provide both pieces of information via ABI.

* update spack package to include fmt for [email protected]: (arbor-sim#1609)

* Docs mechabi (arbor-sim#1610)

* Bit of review on the copy of the mech abi docs.
* Convert api/abi docs to C Domain directives.

* Bug/assorted static analysis (arbor-sim#1615)

* Fix failure to return value in `py_mech_cat_value_iterator `.
* String butchered by formatting.
* Join unnecessary separation of string literals in `arborio/cabelio.cpp`
* Add missing throw in s_expr code.
* Return value, not reference, in `merge_iterator::operator++(int)`.
* Check for self-assignment in `mechanism_catalogue`.
* Initialize all members of `mechanism`.
* Fix index check order in `multi_event_stream` debug output.
* Use coordinator_ from base class in `memory::array`.

Co-authored-by: thorstenhater <[email protected]>
Co-authored-by: Brent Huisman <[email protected]>
Co-authored-by: Ben Cumming <[email protected]>
@akuesters akuesters added this to the v0.6 milestone Nov 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants