Skip to content

Look up shape layers by tag instead of dynamic_cast - #2491

Closed
nikosavola wants to merge 2 commits into
KLayout:masterfrom
nikosavola:nikosavola/push-pwkukoulnkrt
Closed

nikosavola wants to merge 2 commits into
KLayout:masterfrom
nikosavola:nikosavola/push-pwkukoulnkrt

Conversation

@nikosavola

@nikosavola nikosavola commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Shapes keeps each shape type in its own layer object, so every insert and every typed iterator has to find the layer for its type. That lookup previously ran a dynamic_cast over the layers stored so far: dynamic_cast cannot be inlined, walks the RTTI class hierarchy and, on some compilers, compares type names. Replacing it with a pointer comparison makes the lookup almost free, which shows mostly in shape insertion and typed iteration.

  • Shapes::get_layer scanned the layers with a dynamic_cast per element on every insert. Layers carry a per type tag now (a static member of layer_class), and every layer type has exactly one such static.
  • the cast cannot be made efficient instead: it compiles to an opaque runtime call (__dynamic_cast/__RTDynamicCast) that the optimizer cannot inline or fold away, and it walks the RTTI class hierarchy on every call. typeid comparisons are the standard fallback, which would still be an out-of-line runtime call, roughly 10x faster than the cast but 10x slower than a pointer compare, and only guaranteed correct across shared libraries through a name comparison
case before ms after ms speedup
shapes_insert_boxes/200000 4.75 3.53 1.35x
shapes_insert_mixed/200000 29.3 20.1 1.46x
shapes_iterate_mixed/200000 6.26 1.85 3.38x

Measured on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz, g++ -O2 (qmake default), median of 7 repetitions, before and after builds interleaved.

Shapes keeps each shape type in its own layer object, so every insert and every typed iterator has to find the layer for its type. That lookup previously ran a dynamic_cast over the layers stored so far: dynamic_cast cannot be inlined, walks the RTTI class hierarchy and, on some compilers, compares type names. Replacing it with a pointer comparison makes the lookup almost free, which shows mostly in shape insertion and typed iteration.

- `Shapes::get_layer` scanned the layers with a `dynamic_cast` per element on every insert. Layers carry a per type tag now (a static member of `layer_class`), and every layer type has exactly one such static.
- the cast cannot be made efficient instead: it compiles to an opaque runtime call (`__dynamic_cast`/`__RTDynamicCast`) that the optimizer cannot inline or fold away, and it walks the RTTI class hierarchy on every call. `typeid` comparisons are the standard fallback, which would still be an out-of-line runtime call, roughly 10x faster than the cast but 10x slower than a pointer compare, and only guaranteed correct across shared libraries through a name comparison

| case | before ms | after ms | speedup |
|---|---:|---:|---:|
| shapes_insert_boxes/200000 | 4.75 | 3.53 | 1.35x |
| shapes_insert_mixed/200000 | 29.3 | 20.1 | 1.46x |
| shapes_iterate_mixed/200000 | 6.26 | 1.85 | 3.38x |

Measured on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz, g++ -O2 (qmake default), median of 7 repetitions, before and after builds interleaved.
@nikosavola nikosavola closed this Oct 19, 2026
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 this pull request may close these issues.

1 participant