STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_props_performance Namespace Reference

Functions

float _time_us (object call, int n=ITERATIONS)
None test_get_is_within_the_convenience_budget (str prop)
None test_the_hot_path_meets_the_phase_budget (str prop)
None test_accessor_is_faster_than_get (str prop)
None test_tables_are_dramatically_faster_than_the_equation_of_state ()
None test_propssi_would_be_far_slower ()
None test_backends_are_confined_to_their_thread ()
None test_a_shared_tabulated_backend_is_also_thread_safe ()
None test_the_memo_returns_exactly_what_the_backend_would ()
None test_get_and_the_accessor_share_one_entry ()
None test_the_memo_is_bounded ()

Variables

 pytestmark
float HOT_PATH_BUDGET_US = 10.0
float CONVENIENCE_BUDGET_US = 15.0
int ITERATIONS = 20_000
int BATCHES = 5
float _STEP_K = 1.0e-6

Detailed Description

Phase 01's speed gate, and the thread confinement that makes it safe.

Speed is a correctness property here, not a nicety. A transient on a sixty-node
network evaluates properties millions of times over Jacobian assembly; at
CoolProp's convenience API (184 us/call) that solve does not finish, so the
whole layer exists to make it 0.1 us. A regression would not break a test
anywhere else -- it would just quietly make the simulator unusable, months
later, with no obvious culprit.

Thresholds are deliberately loose against the measured numbers so shared CI
runners do not produce flakes. They are ceilings on the *design*, not
benchmarks: the point is to catch someone accidentally routing the hot path
through PropsSI or rebuilding a backend per call, which costs 10-1000x, not to
notice a 20% drift.

Function Documentation

◆ _time_us()

float test_props_performance._time_us ( object call,
int n = ITERATIONS )
protected
Microseconds per call in the fastest of :data:`BATCHES` batches, after a
warm-up that excludes table build.

``call`` takes the iteration index and is expected to use it to move the
state point -- see :data:`_STEP_K`. The index runs on across batches, so no
batch repeats another's state points.

Definition at line 98 of file test_props_performance.py.

References max, and min.

Referenced by test_accessor_is_faster_than_get(), test_get_is_within_the_convenience_budget(), test_propssi_would_be_far_slower(), test_tables_are_dramatically_faster_than_the_equation_of_state(), and test_the_hot_path_meets_the_phase_budget().

◆ test_a_shared_tabulated_backend_is_also_thread_safe()

None test_props_performance.test_a_shared_tabulated_backend_is_also_thread_safe ( )
One measured table, many threads.

Unlike the CoolProp backends, a tabulated one cannot be rebuilt per thread
-- it is parameterised by its data, not by a species name -- so the single
instance is shared and only its cursor is thread-local. This asserts that
actually holds.

Definition at line 236 of file test_props_performance.py.

◆ test_accessor_is_faster_than_get()

None test_props_performance.test_accessor_is_faster_than_get ( str prop)
The bound accessor is the loop API, and must earn that claim.

It hoists everything invariant -- state-pair resolution, which backends
support the property, keyword unpacking -- out of the call. If it ever
stopped being faster, the extra API would be complexity for nothing.

Definition at line 145 of file test_props_performance.py.

References _time_us(), and read().

◆ test_backends_are_confined_to_their_thread()

None test_props_performance.test_backends_are_confined_to_their_thread ( )
Threads must not share a backend cursor.

Backends wrap a mutable cursor: ``update`` moves it, ``value`` reads it.
Two threads sharing one would interleave -- each reading the other's state
point -- and produce plausible wrong numbers with no exception anywhere.

Each thread here queries a *different* state point in a tight loop and
checks its own answer every time. Interleaving would show up as a wrong
density, not as a crash, which is exactly why it needs asserting.

Definition at line 201 of file test_props_performance.py.

◆ test_get_and_the_accessor_share_one_entry()

None test_props_performance.test_get_and_the_accessor_share_one_entry ( )
Otherwise the accessor -- the API this module tells people to use in a
loop -- would be the slower of the two for a repeated point, and its own
advice would be wrong.

Definition at line 315 of file test_props_performance.py.

References read().

◆ test_get_is_within_the_convenience_budget()

None test_props_performance.test_get_is_within_the_convenience_budget ( str prop)
The ergonomic API stays close to a microsecond.

Definition at line 119 of file test_props_performance.py.

References _time_us().

◆ test_propssi_would_be_far_slower()

None test_props_performance.test_propssi_would_be_far_slower ( )
The measurement behind the ban in test_property_call_discipline.

That test forbids ``PropsSI`` in library code on the strength of a number.
This is the number, re-measured, so the rule keeps a live justification
rather than a comment citing a benchmark nobody runs. Tests are exempt from
the ban precisely so this comparison can exist.

Definition at line 177 of file test_props_performance.py.

References _time_us().

◆ test_tables_are_dramatically_faster_than_the_equation_of_state()

None test_props_performance.test_tables_are_dramatically_faster_than_the_equation_of_state ( )
The reason the chain is ordered the way it is.

A loose 3x floor against a measured 10-30x: enough to prove the tabulated
path is actually being taken, without failing on a noisy runner.

Definition at line 162 of file test_props_performance.py.

References _time_us().

◆ test_the_hot_path_meets_the_phase_budget()

None test_props_performance.test_the_hot_path_meets_the_phase_budget ( str prop)
The accessor -- what a solve loop uses -- stays fast.

Phase 01's design target is under a microsecond, met at 0.63 us on the
development host. What is asserted here is the looser ceiling above, so the
test means the same thing on a loaded runner; run with
FEEDTWIN_STRICT_PERF=1 to hold the real target.

Z is included deliberately: it is the gas-side workhorse (COPV, ullage) and
is derived from several reads rather than being one, so it is the property
most likely to drift over.

Definition at line 127 of file test_props_performance.py.

References _time_us(), and read().

◆ test_the_memo_is_bounded()

None test_props_performance.test_the_memo_is_bounded ( )
It is cleared wholesale rather than evicted one at a time, so the only
thing that must hold is that it cannot grow without limit.

Definition at line 335 of file test_props_performance.py.

◆ test_the_memo_returns_exactly_what_the_backend_would()

None test_props_performance.test_the_memo_returns_exactly_what_the_backend_would ( )
The property memo is an *exact* cache, and has to stay one.

No rounding, no tolerance, no quantised state -- a hit returns the number
the backend would have returned, and the only thing saved is asking again.
That is what makes it safe to put in front of a solve: it cannot move an
answer, only shorten the path to it.

Checked against a fluid with no memo of its own, so the comparison is
against the backend rather than against another cache.

Definition at line 276 of file test_props_performance.py.

Variable Documentation

◆ _STEP_K

float test_props_performance._STEP_K = 1.0e-6
protected

Definition at line 95 of file test_props_performance.py.

◆ BATCHES

int test_props_performance.BATCHES = 5

Definition at line 79 of file test_props_performance.py.

◆ CONVENIENCE_BUDGET_US

float test_props_performance.CONVENIENCE_BUDGET_US = 15.0

Definition at line 59 of file test_props_performance.py.

◆ HOT_PATH_BUDGET_US

float test_props_performance.HOT_PATH_BUDGET_US = 10.0

Definition at line 58 of file test_props_performance.py.

◆ ITERATIONS

int test_props_performance.ITERATIONS = 20_000

Definition at line 70 of file test_props_performance.py.

◆ pytestmark

test_props_performance.pytestmark
Initial value:
1= pytest.mark.skipif(
2 "PYTEST_XDIST_WORKER" in os.environ, reason="timed on its own, not in parallel"
3)

Definition at line 29 of file test_props_performance.py.