Performance Concern: ABI Datatype Converter Uses Linear Search in Hot Path
Summary
The standard ABI datatype converter functions currently use a linear if/else if chain to convert between ABI handle values and internal Open MPI datatype pointers. This chain scans through ~60-70 predefined datatypes (50 from PREDEFINED_DATATYPES plus ~18 from PREDEFINED_OPTIONAL_FORTRAN_DATATYPES). Because these converter functions are marked with __opal_attribute_always_inline__, this linear search is inlined into every standard-ABI call site that converts a datatype, including the critical point-to-point and collective communication hot paths.
Impact
This design has two primary concerns:
-
Latency: User-defined datatypes must scan through the entire predefined list before falling through to the default case. In a project where short-message latency is a headline metric, this overhead is measurable.
-
Code size: The ~60-70-branch scan is duplicated at every ABI converter call site due to forced inlining, leading to code bloat.
Current Implementation
The converter is generated in ompi/mpi/bindings/ompi_bindings/c.py:247 and produces code similar to:
static inline MPI_Datatype convert_datatype_abi_to_ompi(MPI_Datatype datatype) {
if (datatype == MPI_CHAR_ABI_INTERNAL) return ompi_mpi_char;
else if (datatype == MPI_SHORT_ABI_INTERNAL) return ompi_mpi_short;
else if (datatype == MPI_INT_ABI_INTERNAL) return ompi_mpi_int;
// ... ~60-70 more comparisons ...
else return (MPI_Datatype) datatype; // user-defined or pass-through
}
Attempted Solutions and Constraints
Initial proposal: Generate a switch statement over the sequential ABI handle values to enable compiler jump table optimization:
switch ((intptr_t) datatype) {
case <abi_value_1>: return <ompi_name_1>;
case <abi_value_2>: return <ompi_name_2>;
// ...
default: return (MPI_Datatype) datatype;
}
Problem: This approach is not portable. The predefined ABI handle constants are defined as pointer-typed compile-time constants (e.g., ((MPI_Datatype)((void*)N))). According to C11 §6.6, switch case labels must be integer constant expressions, which cannot contain casts from pointer types. Therefore, case (intptr_t) MPI_INT_ABI_INTERNAL: is not a valid constant expression and will not compile.
The linear if/else if chain is currently the only portable form for comparing these pointer constants.
Additional Complexity
The converter functions also include:
- An
offset fast-path optimization for certain handle ranges
- A pass-through default case for user-defined types
- Multiple converter functions for different handle types (datatypes, communicators, groups, etc.)
This means the handle space is not dense or contiguous, which would complicate any perfect-hash or jump-table approach.
Potential Future Optimizations
If profiling demonstrates that this linear search appears in latency-critical paths, possible optimization approaches include:
- Perfect hash function: Design a minimal perfect hash for the ~60-70 predefined constants
- Range check optimization: Exploit the fact that ABI handles use the reserved 1-4095 integer band; could use a lookup table for this range
- Compiler-specific extensions: Investigate if specific compilers offer pragmas/attributes to optimize the linear chain
- Binary search: Convert the linear chain to a binary search tree (though this would require sortable handle values)
Questions for Discussion
- Has anyone profiled whether this converter overhead is measurable in real-world MPI application latency?
- Would removing the
__opal_attribute_always_inline__ attribute (allowing the compiler to decide) help with code size without hurting latency?
- Are there other handle types (communicators, groups, etc.) with similar concerns?
- Should we add this as a known performance consideration in the ABI documentation?
References
Labels
performance, abi, optimization, latency
Performance Concern: ABI Datatype Converter Uses Linear Search in Hot Path
Summary
The standard ABI datatype converter functions currently use a linear
if/else ifchain to convert between ABI handle values and internal Open MPI datatype pointers. This chain scans through ~60-70 predefined datatypes (50 fromPREDEFINED_DATATYPESplus ~18 fromPREDEFINED_OPTIONAL_FORTRAN_DATATYPES). Because these converter functions are marked with__opal_attribute_always_inline__, this linear search is inlined into every standard-ABI call site that converts a datatype, including the critical point-to-point and collective communication hot paths.Impact
This design has two primary concerns:
Latency: User-defined datatypes must scan through the entire predefined list before falling through to the default case. In a project where short-message latency is a headline metric, this overhead is measurable.
Code size: The ~60-70-branch scan is duplicated at every ABI converter call site due to forced inlining, leading to code bloat.
Current Implementation
The converter is generated in
ompi/mpi/bindings/ompi_bindings/c.py:247and produces code similar to:Attempted Solutions and Constraints
Initial proposal: Generate a
switchstatement over the sequential ABI handle values to enable compiler jump table optimization:Problem: This approach is not portable. The predefined ABI handle constants are defined as pointer-typed compile-time constants (e.g.,
((MPI_Datatype)((void*)N))). According to C11 §6.6,switchcaselabels must be integer constant expressions, which cannot contain casts from pointer types. Therefore,case (intptr_t) MPI_INT_ABI_INTERNAL:is not a valid constant expression and will not compile.The linear
if/else ifchain is currently the only portable form for comparing these pointer constants.Additional Complexity
The converter functions also include:
offsetfast-path optimization for certain handle rangesThis means the handle space is not dense or contiguous, which would complicate any perfect-hash or jump-table approach.
Potential Future Optimizations
If profiling demonstrates that this linear search appears in latency-critical paths, possible optimization approaches include:
Questions for Discussion
__opal_attribute_always_inline__attribute (allowing the compiler to decide) help with code size without hurting latency?References
ompi/mpi/bindings/ompi_bindings/c.py:247(generator)generic_convertfunction for all handle typesLabels
performance,abi,optimization,latency