diff --git a/benchmark/results/perf_lazy_catalog_20260929/README.md b/benchmark/results/perf_lazy_catalog_20260929/README.md new file mode 100644 index 0000000000..b0ea45417a --- /dev/null +++ b/benchmark/results/perf_lazy_catalog_20260929/README.md @@ -0,0 +1,28 @@ +# Lazy execution-server function catalog: official30 1w_1t (2026-09-29) + +This report accompanies the lazy catalog initialization change for [OpenHCS issue #165](https://github.com/OpenHCSDev/openhcs/issues/165). Both fresh-server runs used the same CPU-only integration checkout and native CP 4.2.8.1 reference. The control CSV is the completed direct-docstring-parser run; the new run changes only execution-server catalog initialization and its import preflight. Each observation owns a fresh ZMQ server, so `total_seconds` includes that server's startup and shutdown. The 1w_1t preset uses one well and one OpenHCS thread. + +All 30 cases succeeded on the final code path. Every case had a lower total, with a paired median saving of 1.171 seconds. All 110 files under the produced `results` directories were byte-identical to the control outputs. + +| Sum over 30 observations (seconds) | Control fresh | Lazy catalog fresh | Difference | +| --- | ---: | ---: | ---: | +| Server compilation jobs | 58.632 | 83.759 | +25.126 | +| Server execution jobs | 93.115 | 89.219 | -3.896 | +| Other measured time (`total - compile - execute`) | 160.110 | 101.289 | -58.822 | +| Per-observation total | 311.858 | 274.266 | **-37.592** | + +The patch moves some registry work into compilation; the execution-job difference is run variation, not a claimed effect of this initialization change. A separately timed full CLI invocation took 296.684 seconds. That wall time includes source preparation outside the per-observation timer and has no matched control wall measurement. The native CP total-phase reference sums to 1,319.357 seconds, making its 5× reference line 263.871 seconds; the new per-observation sum remains 10.395 seconds above that line. The native total-phase and OpenHCS well-throughput timers have different preparation boundaries, so the line is a reference rather than a like-for-like end-to-end ratio. + +`fresh_total_breakdown.png` shows the measured time shift and native reference. `lazy_native_execution_speedup.png` and `lazy_native_execution_cdf.png` show execution-only ratios relative to native CP; the patch does not target those ratios. The completed multi-well native-relative scaling figures predate this startup-only change and remain at `/home/ts/code/projects/openhcs-benchmark-runs/official30-four-modes-native-relative-fresh-v2-20260928`. + +Validation: 104 focused tests passed after the final import-guard change. A live client connected in 1.795 seconds and fetched the complete 266-function catalog on demand in a further 1.616 seconds. The benchmark's ordinary ZMQ outcome and progress evidence path was used throughout. + +Fresh benchmark command: + +```bash +OPENHCS_CPU_ONLY=true .venv/bin/python scripts/benchmark_cppipe_well_throughput.py \ + --manifest benchmark/manifests/official30_portable_axis1.json \ + --output-dir /path/to/output \ + --native-summary-csv benchmark/results/perf_lazy_catalog_20260929/native_cp_summary.csv \ + --mode 1w_1t +``` diff --git a/benchmark/results/perf_lazy_catalog_20260929/control_fresh.csv b/benchmark/results/perf_lazy_catalog_20260929/control_fresh.csv new file mode 100644 index 0000000000..d81a262e71 --- /dev/null +++ b/benchmark/results/perf_lazy_catalog_20260929/control_fresh.csv @@ -0,0 +1,31 @@ +case_name,mode_name,worker_count,well_count,compile_seconds,prepare_seconds,execute_seconds,total_seconds,wells_per_second,successful_wells,native_single_sample_execution_seconds,projected_native_execution_seconds,projected_execution_speedup,peak_memory_mb,status,memory_limit_mb,error_message,execution_route,server_lifecycle,run_input_sha256 +ExampleColocalization,1w_1t,1,1,2.2928268909454346,0.0,3.9878079891204834,11.62109541380778,0.25076433036098894,1,37.035618890076876,37.035618890076876,9.287212170474922,1344.6640625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleCometAssay,1w_1t,1,1,2.0185365676879883,0.0,2.8911311626434326,9.938480501063168,0.3458853797161092,1,13.304211451206356,13.304211451206356,4.601732229623919,1263.62109375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleFly,1w_1t,1,1,2.2488327026367188,0.0,2.1445205211639404,10.066884044092149,0.46630470080894776,1,8.094948755111545,8.094948755111545,3.774712657316053,946.58203125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleFlyURL,1w_1t,1,1,1.9677801132202148,0.0,1.019911527633667,8.41873428830877,0.9804772011158023,1,4.900778113398701,4.900778113398701,4.80510120791474,883.3203125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleHuman,1w_1t,1,1,1.923978567123413,0.0,1.0841801166534424,8.417618672829121,0.9223559670940262,1,4.420192994643003,4.420192994643003,4.0769913843161865,852.3828125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleIlluminationCorrection_Example1_AllMethod,1w_1t,1,1,1.5901720523834229,0.0,4.088348627090454,10.868056402076036,0.24459753587884892,1,946.6214662808925,946.6214662808925,231.54127806232916,1269.7265625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleIlluminationCorrection_Example1_EachMethod,1w_1t,1,1,1.5006680488586426,0.0,0.17162799835205078,6.682493566069752,5.826555163504015,1,4.54475614009425,4.54475614009425,26.48027235493273,790.26953125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleIlluminationCorrection_Example2,1w_1t,1,1,1.881812334060669,0.0,2.101905107498169,9.374385974835604,0.47575887057540306,1,48.04419008316472,48.04419008316472,22.85744961167643,995.20703125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleIlluminationCorrection_Example3,1w_1t,1,1,1.8129961490631104,0.0,0.7289443016052246,7.743487971834838,1.3718469268473292,1,2.1520505752414465,2.1520505752414465,2.9522839680650055,827.015625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleImagingFlowCytometryObjectsInGrid,1w_1t,1,1,2.560279607772827,0.0,23.013134479522705,30.937172566074878,0.04345344615657676,1,61.60158259188756,61.60158259188756,2.6768010523165024,2518.06640625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleNeighbors,1w_1t,1,1,1.74615478515625,0.0,1.3547496795654297,8.286614634096622,0.7381437435148723,1,4.5466188420541584,4.5466188420541584,3.3560582524091105,901.8671875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExamplePercentPositive,1w_1t,1,1,1.7290244102478027,0.0,0.42984962463378906,7.382856552023441,2.326394959288266,1,2.5050158216618,2.5050158216618,5.827656180451365,842.484375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleSpeckles,1w_1t,1,1,1.637129306793213,0.0,0.822502613067627,7.375865648966283,1.2158016085448948,1,3.179383432958275,3.179383432958275,3.86549949197166,976.84765625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleTrackObjects,1w_1t,1,1,1.716566562652588,0.0,3.287581205368042,10.32921796804294,0.3041749960022815,1,9.587221414782107,9.587221414782107,2.916193035514335,1670.55078125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleTumor,1w_1t,1,1,1.6034469604492188,0.0,0.8798391819000244,7.372601582668722,1.1365713423224535,1,4.360487373080105,4.360487373080105,4.956004986801763,1072.33203125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleUntangleAndStraightenWorms,1w_1t,1,1,2.0078978538513184,0.0,1.2335312366485596,8.449991872068495,0.8106807272403965,1,3.4416790287941694,3.4416790287941694,2.790102857990879,888.4375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleUntangleWorms,1w_1t,1,1,2.0958409309387207,0.0,2.062998056411743,10.002663507126272,0.48473143098318805,1,4.618807300925255,4.618807300925255,2.238881072413095,934.63671875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleUntangleWormsBrightField,1w_1t,1,1,2.1711883544921875,0.0,3.5632073879241943,11.496679156087339,0.2806460278986362,1,7.377188352867961,7.377188352867961,2.0703786082924758,1045.79296875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleVitra,1w_1t,1,1,1.8910613059997559,0.0,1.2376892566680908,8.538714677095413,0.8079572433972968,1,4.122247557155788,4.122247557155788,3.330599772880831,960.13671875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleWoundHealing,1w_1t,1,1,1.5049092769622803,0.0,2.042593240737915,8.341959646902978,0.4895737340434634,1,4.702593941241503,4.702593941241503,2.3022664755037696,1272.14453125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleYeastColonies,1w_1t,1,1,2.1904988288879395,0.0,2.0774261951446533,10.033609306905419,0.48136487464016453,1,12.096297012176365,12.096297012176365,5.8227324948764725,1121.2578125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +ExampleYeastPatches,1w_1t,1,1,2.182415246963501,0.0,2.6008615493774414,10.576405676081777,0.3844879787005065,1,5.990533472970128,5.990533472970128,2.30328810636001,1177.07421875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_3d_monolayer,1w_1t,1,1,2.876032829284668,0.0,10.151128053665161,18.547642110381275,0.09851121911903579,1,14.058660756796598,14.058660756796598,1.3849358103329792,1453.578125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_advanced_segmentation_final,1w_1t,1,1,3.3548684120178223,0.0,10.546051740646362,20.040677363052964,0.09482221636992562,1,36.253466514870524,36.253466514870524,3.437634046032906,1147.62890625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_quality_control,1w_1t,1,1,1.3814642429351807,0.0,0.6194014549255371,7.224075731821358,1.6144618196291087,1,4.787729676347226,4.787729676347226,7.729606765167826,855.1953125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_beginner_segmentation_final,1w_1t,1,1,2.4305648803710938,0.0,6.888291358947754,14.83460831688717,0.14517388244633697,1,16.074153264053166,16.074153264053166,2.333547236380058,1046.05078125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_pixel_based_classification,1w_1t,1,1,1.6967191696166992,0.0,0.8674452304840088,7.88223431725055,1.1528105347262438,1,4.208680049050599,4.208680049050599,4.851810697837696,1035.89453125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_translocation_final,1w_1t,1,1,1.854893445968628,0.0,0.8097820281982422,7.803559121210128,1.2349002141045178,1,3.004684013314545,3.004684013314545,3.710484931358554,931.90625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp4_supplement_combine_objects,1w_1t,1,1,1.3613243103027344,0.0,0.14962530136108398,6.642139645293355,6.683361643407789,1,1.8334413380362093,1.8334413380362093,12.253551514069457,825.0390625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 +cp_tutorial_translocation_start,1w_1t,1,1,1.402390956878662,0.0,0.25930023193359375,6.627473204862326,3.856533380410157,1,2.1760300607420504,2.1760300607420504,8.39193256602766,866.1328125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,dbed67c7d17c4e678119e0e634ed4eb2a7c6b0d51b7b581196590fed56cc8818 diff --git a/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.png b/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.png new file mode 100644 index 0000000000..4c3fc64c77 Binary files /dev/null and b/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.png differ diff --git a/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.svg b/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.svg new file mode 100644 index 0000000000..1d79493efa --- /dev/null +++ b/benchmark/results/perf_lazy_catalog_20260929/fresh_total_breakdown.svg @@ -0,0 +1,1622 @@ + + + + + + + + 2026-09-29T03:50:31.886197 + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/benchmark/results/perf_lazy_catalog_20260929/lazy_fresh.csv b/benchmark/results/perf_lazy_catalog_20260929/lazy_fresh.csv new file mode 100644 index 0000000000..a2fe0eb6a4 --- /dev/null +++ b/benchmark/results/perf_lazy_catalog_20260929/lazy_fresh.csv @@ -0,0 +1,31 @@ +case_name,mode_name,worker_count,well_count,compile_seconds,prepare_seconds,execute_seconds,total_seconds,wells_per_second,successful_wells,native_single_sample_execution_seconds,projected_native_execution_seconds,projected_execution_speedup,peak_memory_mb,status,memory_limit_mb,error_message,execution_route,server_lifecycle,run_input_sha256 +ExampleColocalization,1w_1t,1,1,3.3051583766937256,0.0,3.6666457653045654,10.503191142808646,0.2727288273829027,1,37.035618890076876,37.035618890076876,10.100680911290747,1321.0546875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleCometAssay,1w_1t,1,1,3.030191659927368,0.0,2.557861566543579,8.700058875139803,0.39095157184416873,1,13.304211451206356,13.304211451206356,5.2013023789963135,1231.8828125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleFly,1w_1t,1,1,2.995745897293091,0.0,2.165245532989502,8.828781486023217,0.46184138693006527,1,8.094948755111545,8.094948755111545,3.7385823601885213,910.81640625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleFlyURL,1w_1t,1,1,2.714168071746826,0.0,1.01365065574646,7.007350472267717,0.9865331752423052,1,4.900778113398701,4.900778113398701,4.834780193369214,828.10546875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleHuman,1w_1t,1,1,2.648146629333496,0.0,1.2065091133117676,7.4798929137177765,0.8288375023169803,1,4.420192994643003,4.420192994643003,3.6636217214389197,799.84765625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleIlluminationCorrection_Example1_AllMethod,1w_1t,1,1,2.5387473106384277,0.0,2.738597869873047,8.265587457921356,0.36515036070131646,1,946.6214662808925,946.6214662808925,345.659169860077,1312.6953125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleIlluminationCorrection_Example1_EachMethod,1w_1t,1,1,2.454306125640869,0.0,0.1676487922668457,5.733787109144032,5.964850605116828,1,4.54475614009425,4.54475614009425,27.10879141234961,759.90625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleIlluminationCorrection_Example2,1w_1t,1,1,2.9105353355407715,0.0,1.7648985385894775,7.91399956215173,0.5666048093615675,1,48.04419008316472,48.04419008316472,27.22206916300246,1000.71875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleIlluminationCorrection_Example3,1w_1t,1,1,2.9293508529663086,0.0,0.8705692291259766,6.964360218960792,1.148673725814968,1,2.1520505752414465,2.1520505752414465,2.472003952404837,794.453125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleImagingFlowCytometryObjectsInGrid,1w_1t,1,1,3.428496837615967,0.0,21.203369855880737,28.11586388805881,0.04716231461305434,1,61.60158259188756,61.60158259188756,2.9052732188606525,2432.64453125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleNeighbors,1w_1t,1,1,2.427239179611206,0.0,1.3531577587127686,6.883681884035468,0.7390121318531844,1,4.5466188420541584,4.5466188420541584,3.3600064831903005,836.2109375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExamplePercentPositive,1w_1t,1,1,2.431612253189087,0.0,0.44060468673706055,6.032959253992885,2.269608177356428,1,2.5050158216618,2.5050158216618,5.685404393250853,793.640625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleSpeckles,1w_1t,1,1,2.3191630840301514,0.0,0.8265681266784668,6.392104679252952,1.2098216320274322,1,3.179383432958275,3.179383432958275,3.8464868537025603,895.76171875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleTrackObjects,1w_1t,1,1,2.427124261856079,0.0,3.2250688076019287,8.820541792083532,0.310070903803002,1,9.587221414782107,9.587221414782107,2.9727184090409833,1596.9609375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleTumor,1w_1t,1,1,2.293205499649048,0.0,0.8080277442932129,6.27411754289642,1.2375812675525226,1,4.360487373080105,4.360487373080105,5.396457490323245,953.5,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleUntangleAndStraightenWorms,1w_1t,1,1,3.00498628616333,0.0,1.243178367614746,7.572613257914782,0.8043898012146672,1,3.4416790287941694,3.4416790287941694,2.7684515098164306,863.2265625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleUntangleWorms,1w_1t,1,1,3.205960750579834,0.0,2.200486660003662,8.85065891686827,0.4544449271954849,1,4.618807300925255,4.618807300925255,2.0989935475989516,924.0546875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleUntangleWormsBrightField,1w_1t,1,1,3.292342185974121,0.0,3.7398955821990967,10.747698435094208,0.267387144379039,1,7.377188352867961,7.377188352867961,1.9725653272196704,1023.015625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleVitra,1w_1t,1,1,2.898782253265381,0.0,1.2529144287109375,7.474349890835583,0.7981391043830912,1,4.122247557155788,4.122247557155788,3.290126973313706,934.3515625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleWoundHealing,1w_1t,1,1,2.021925210952759,0.0,1.4669053554534912,6.312976646237075,0.6817072391768941,1,4.702593941241503,4.702593941241503,3.205792332653734,1197.5,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleYeastColonies,1w_1t,1,1,3.246541738510132,0.0,2.0734219551086426,9.27478964207694,0.48229449752672376,1,12.096297012176365,12.096297012176365,5.83397748942161,1116.33203125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +ExampleYeastPatches,1w_1t,1,1,3.228555202484131,0.0,2.6230390071868896,9.73673579795286,0.3812371822378891,1,5.990533472970128,5.990533472970128,2.2838141013368873,1104.23828125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_3d_monolayer,1w_1t,1,1,3.563236713409424,0.0,10.151130676269531,17.0197413158603,0.09851119366808239,1,14.058660756796598,14.058660756796598,1.3849354525266595,1395.6328125,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_advanced_segmentation_final,1w_1t,1,1,4.281343460083008,0.0,10.925135135650635,19.630296141374856,0.091532048581882,1,36.253466514870524,36.253466514870524,3.3183540583007614,1098.890625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_quality_control,1w_1t,1,1,1.9249956607818604,0.0,0.6149721145629883,6.064614792820066,1.6260899906179005,1,4.787729676347226,4.787729676347226,7.785279304492504,804.34765625,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_beginner_segmentation_final,1w_1t,1,1,3.191765785217285,0.0,6.749016046524048,14.025305974297225,0.14816974698334448,1,16.074153264053166,16.074153264053166,2.381703222106258,969.4296875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_pixel_based_classification,1w_1t,1,1,2.38506817817688,0.0,0.8117902278900146,6.388479145243764,1.2318453285637296,1,4.208680049050599,4.208680049050599,5.184442857842349,989.24609375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_translocation_final,1w_1t,1,1,2.5704596042633057,0.0,0.9488544464111328,6.581289496738464,1.0539024228450589,1,3.004684013314545,3.004684013314545,3.1666437615160143,877.51171875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp4_supplement_combine_objects,1w_1t,1,1,2.008012294769287,0.0,0.15258502960205078,5.45942115271464,6.553722882304043,1,1.8334413380362093,1.8334413380362093,12.015866450450048,775.21875,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be +cp_tutorial_translocation_start,1w_1t,1,1,2.0814247131347656,0.0,0.25717926025390625,5.210829641204327,3.88833842593966,1,2.1760300607420504,2.1760300607420504,8.461141301183128,801.15234375,success,,,ordinary-zmq-outcomes-v1,fresh-per-observation,6d8da1f92a4266752424d89e1bf8a178d54dd542f74cf87ffca80c48113a99be diff --git a/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_cdf.png b/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_cdf.png new file mode 100644 index 0000000000..9e220c446d Binary files /dev/null and b/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_cdf.png differ diff --git a/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_speedup.png b/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_speedup.png new file mode 100644 index 0000000000..a8709fd103 Binary files /dev/null and b/benchmark/results/perf_lazy_catalog_20260929/lazy_native_execution_speedup.png differ diff --git a/benchmark/results/perf_lazy_catalog_20260929/native_cp_summary.csv b/benchmark/results/perf_lazy_catalog_20260929/native_cp_summary.csv new file mode 100644 index 0000000000..32394feaf5 --- /dev/null +++ b/benchmark/results/perf_lazy_catalog_20260929/native_cp_summary.csv @@ -0,0 +1,31 @@ +case_name,assay_category,module_category,n,equivalent_count,median_native_execution_seconds,median_openhcs_execution_seconds,median_native_total_phase_seconds,median_openhcs_total_phase_seconds,median_native_peak_memory_mb,median_openhcs_peak_memory_mb,median_speedup,median_total_phase_speedup,speedup_target,meets_execution_speedup_target,meets_total_phase_speedup_target,meets_speedup_target,min_parity_accuracy +ExampleColocalization,,,1,1,37.035618890076876,17.959853410720825,39.35332336090505,26.953527031932026,,,2.062133695811186,1.4600435525296152,5.0,False,False,False,1.0 +ExampleCometAssay,,,1,1,13.304211451206356,2.57122540473938,14.30461646290496,8.059360695071518,,,5.1742688239947885,1.774907093021976,5.0,True,False,True,1.0 +ExampleFly,,,1,1,8.094948755111545,2.572516441345215,9.810052920132875,16.424860467668623,,,3.1467043805864083,0.5972685697661414,5.0,False,False,False,1.0 +ExampleFlyURL,,,1,1,4.900778113398701,1.587127923965454,6.021430939435959,8.042698163539171,,,3.0878280442285084,0.7486829440813233,5.0,False,False,False,1.0 +ExampleHuman,,,1,1,4.420192994643003,2.1705381870269775,5.455457073636353,11.446641855407506,,,2.036450232049322,0.47659891368568863,5.0,False,False,False,1.0 +ExampleIlluminationCorrection_Example1_AllMethod,,,1,1,946.6214662808925,6.739018201828003,946.862535060849,9.857038384769112,,,140.46875048120737,96.0595361507287,5.0,True,True,True,1.0 +ExampleIlluminationCorrection_Example1_EachMethod,,,1,1,4.54475614009425,0.2049102783203125,4.6763526406139135,3.222155754920095,,,22.17924926630552,1.4513117913289328,5.0,True,False,True,1.0 +ExampleIlluminationCorrection_Example2,,,1,1,48.04419008316472,2.1667985916137695,48.96783200325444,6.28821934806183,,,22.17289150413504,7.787233442858101,5.0,True,True,True,1.0 +ExampleIlluminationCorrection_Example3,,,1,1,2.1520505752414465,2.0806398391723633,2.7176199601963162,6.080346302129328,,,1.0343215268325772,0.4469515098580174,5.0,False,False,False,1.0 +ExampleImagingFlowCytometryObjectsInGrid,,,1,1,61.60158259188756,36.9328293800354,64.14507223013788,98.14512260677293,,,1.667935644951893,0.6535737133585411,5.0,False,False,False,1.0 +ExampleNeighbors,,,1,1,4.5466188420541584,2.789321184158325,5.234509553294629,14.297266059555113,,,1.6300090745648914,0.3661196155607886,5.0,False,False,False,1.0 +ExamplePercentPositive,,,1,1,2.5050158216618,0.9022600650787354,3.3950326037593186,5.097386521752924,,,2.776378916253155,0.666033974326085,5.0,False,False,False,1.0 +ExampleSpeckles,,,1,1,3.179383432958275,0.9791054725646973,3.897916650865227,4.4375677248463035,,,3.2472328283796696,0.8783903463693576,5.0,False,False,False,1.0 +ExampleTrackObjects,,,1,1,9.587221414782107,3.7285819053649902,10.388708942104131,7.917243284638971,,,2.5712782119623614,1.3121623990335494,5.0,False,False,False,1.0 +ExampleTumor,,,1,1,4.360487373080105,1.1599302291870117,4.82744375616312,4.7034944533370435,,,3.759266948440808,1.026352598914651,5.0,False,False,False,1.0 +ExampleUntangleAndStraightenWorms,,,1,1,3.4416790287941694,2.619752883911133,4.3137561599723995,6.864017297513783,,,1.3137418608948912,0.628459395277877,5.0,False,False,False,1.0 +ExampleUntangleWorms,,,1,1,4.618807300925255,4.506817102432251,5.426323775202036,8.279627295210958,,,1.0248490666356451,0.655382613459025,5.0,False,False,False,1.0 +ExampleUntangleWormsBrightField,,,1,1,7.377188352867961,7.678912878036499,8.610389334149659,13.188176837284118,,,0.960707390491232,0.6528870093557847,5.0,False,False,False,1.0 +ExampleVitra,,,1,1,4.122247557155788,3.466949462890625,4.8610909334383905,12.178178021218628,,,1.1890128775395525,0.39916405598347116,5.0,False,False,False,1.0 +ExampleWoundHealing,,,1,1,4.702593941241503,2.663236141204834,5.02863251324743,5.365988084580749,,,1.7657442644624348,0.9371307639868386,5.0,False,False,False,1.0 +ExampleYeastColonies,,,1,1,12.096297012176365,4.731346368789673,13.198869303334504,16.91469065612182,,,2.5566289316651156,0.7803198752888529,5.0,False,False,False,1.0 +ExampleYeastPatches,,,1,1,5.990533472970128,5.013910531997681,7.104359833989292,10.384889775887132,,,1.1947826820482443,0.684105463544258,5.0,False,False,False,1.0 +cp4_supplement_combine_objects,Object-combination benchmark,Object set algebra,1,1,1.8334413380362093,0.18567681312561035,1.9359478713013232,2.6644571498036385,,,9.874368841067335,0.7265824753248503,5.0,True,False,True,1.0 +cp_tutorial_3d_monolayer,3D monolayer morphology,3D segmentation + measurement,1,1,14.058660756796598,13.644547939300537,15.750524973962456,19.772331691812724,,,1.0303500577181666,0.7965942115205559,5.0,False,False,False,1.0 +cp_tutorial_advanced_segmentation_final,Cell Painting morphology,Advanced segmentation + measurement,1,1,36.253466514870524,21.642004013061523,45.43951385235414,50.77297239517793,,,1.67514369246908,0.8949547703980725,5.0,False,False,False,1.0 +cp_tutorial_beginner_segmentation_final,Cell morphology,Segmentation + intensity measurement,1,1,16.074153264053166,8.612112760543823,18.3711662706919,36.70200763596222,,,1.8664587553586724,0.5005493555805115,5.0,False,False,False,1.0 +cp_tutorial_pixel_based_classification,Pixel classification,Pixel classification,1,1,4.208680049050599,0.9723286628723145,4.7289649839513,4.328970203641802,,,4.328454163449134,1.092399522633119,5.0,False,False,False,1.0 +cp_tutorial_quality_control,Cell Painting quality control,Image quality measurement,1,1,4.787729676347226,1.2474637031555176,8.168980250600725,7.120973748154938,,,3.8379711283273736,1.147171796935402,5.0,False,False,False,1.0 +cp_tutorial_translocation_final,Translocation assay,Segmentation + classification,1,1,3.004684013314545,1.9986815452575684,3.8793991575948894,5.977722632698715,,,1.5033330449485558,0.6489761061134227,5.0,False,False,False,1.0 +cp_tutorial_translocation_start,Translocation assay,Starter nuclei segmentation,1,1,2.1760300607420504,0.33385229110717773,2.4812272735871375,2.8119246223941445,,,6.517942571325569,0.882394660876278,5.0,True,False,True,1.0 diff --git a/openhcs/processing/backends/lib_registry/registry_service.py b/openhcs/processing/backends/lib_registry/registry_service.py index 8828c56054..7677015900 100644 --- a/openhcs/processing/backends/lib_registry/registry_service.py +++ b/openhcs/processing/backends/lib_registry/registry_service.py @@ -64,31 +64,34 @@ def get_all_functions_with_metadata( cancellation: OperationCancellation | None = None, ) -> Dict[str, FunctionMetadata]: """Get unified metadata for all functions from all registries.""" - if cls._metadata_cache is not None: - logger.debug( - f"🎯 REGISTRY SERVICE: Using cached metadata ({len(cls._metadata_cache)} functions)" - ) - return cls._metadata_cache + with cls._registry_inventory_lock: + if cls._metadata_cache is not None: + logger.debug( + f"🎯 REGISTRY SERVICE: Using cached metadata ({len(cls._metadata_cache)} functions)" + ) + return cls._metadata_cache - emit_status = status_callback or logger.debug - emit_status("Loading cached function catalog") - registry_instances = cls._available_registry_instances() - cached_functions = cls._load_valid_persistent_catalog(registry_instances) - if cached_functions is None: - cls._prepare_persistent_catalog( - status_callback=emit_status, - cancellation=cancellation, - ) - emit_status("Loading the prepared function catalog") + emit_status = status_callback or logger.debug + emit_status("Loading cached function catalog") + registry_instances = cls._available_registry_instances() cached_functions = cls._load_valid_persistent_catalog(registry_instances) - if cached_functions is None: - raise RuntimeError( - "Function registry preparation completed without producing a valid " - "persistent catalog." - ) - cls._metadata_cache = cached_functions - emit_status(f"Function catalog ready ({len(cached_functions)} functions)") - return cached_functions + if cached_functions is None: + cls._prepare_persistent_catalog( + status_callback=emit_status, + cancellation=cancellation, + ) + emit_status("Loading the prepared function catalog") + cached_functions = cls._load_valid_persistent_catalog( + registry_instances + ) + if cached_functions is None: + raise RuntimeError( + "Function registry preparation completed without producing a valid " + "persistent catalog." + ) + cls._metadata_cache = cached_functions + emit_status(f"Function catalog ready ({len(cached_functions)} functions)") + return cached_functions @classmethod def cached_metadata_snapshot(cls) -> Dict[str, FunctionMetadata]: diff --git a/openhcs/processing/func_registry.py b/openhcs/processing/func_registry.py index ae0e601379..394e9bc204 100644 --- a/openhcs/processing/func_registry.py +++ b/openhcs/processing/func_registry.py @@ -8,15 +8,18 @@ from __future__ import annotations +import ast import logging import sys import threading import types from collections.abc import Callable, Mapping +from importlib.machinery import PathFinder from typing import Any from arraybridge.types import VALID_MEMORY_TYPES +import openhcs from openhcs.core.callable_contract import CallableContract logger = logging.getLogger(__name__) @@ -27,6 +30,56 @@ _external_projection_modules: set[str] = set() +def pipeline_source_requires_import_projection(source: str) -> bool: + """Admit direct imports without building the external virtual-module view. + + The registry owns virtual ``openhcs.`` modules, while real + OpenHCS package children are importable without registry initialization. + Dynamic imports conservatively retain the full projection. + """ + + requested_modules: set[str] = set() + for node in ast.walk(ast.parse(source)): + if isinstance(node, ast.Import): + requested_modules.update(alias.name for alias in node.names) + elif isinstance(node, ast.ImportFrom): + if node.level: + return True + if node.module == "openhcs": + requested_modules.update( + f"openhcs.{alias.name}" for alias in node.names + ) + elif node.module is not None: + requested_modules.add(node.module) + elif isinstance(node, ast.Call) and ( + ( + isinstance(node.func, ast.Name) + and node.func.id in {"__import__", "exec", "eval"} + ) + or ( + isinstance(node.func, ast.Attribute) + and node.func.attr == "import_module" + ) + ): + if not node.args or not isinstance(node.args[0], ast.Constant): + return True + imported = node.args[0].value + if not isinstance(imported, str): + return True + if isinstance(node.func, ast.Name) and node.func.id in {"exec", "eval"}: + return True + requested_modules.add(imported) + + for module_name in requested_modules: + parts = module_name.split(".") + if len(parts) < 2 or parts[0] != "openhcs": + continue + top_level_module = f"openhcs.{parts[1]}" + if PathFinder.find_spec(top_level_module, openhcs.__path__) is None: + return True + return False + + def _create_external_virtual_modules(all_functions: Mapping[str, Any]) -> None: """Reconcile the canonical external-callable import projection.""" diff --git a/openhcs/runtime/zmq_execution_server.py b/openhcs/runtime/zmq_execution_server.py index c1b80db0a0..9b60545384 100644 --- a/openhcs/runtime/zmq_execution_server.py +++ b/openhcs/runtime/zmq_execution_server.py @@ -393,26 +393,12 @@ def _execute_pipeline( ): logger.info("[%s] Starting plate %s", execution_id, request_payload.plate_id) - self._function_catalog_preparation.wait_until_ready() - import openhcs.processing.func_registry as func_registry_module - logger.info( - "[%s] Registry initialized status BEFORE check: %s", - execution_id, - func_registry_module._registry_initialized, - ) - with func_registry_module._registry_lock: - if not func_registry_module._registry_initialized: - logger.info("[%s] Initializing registry...", execution_id) - func_registry_module._auto_initialize_registry() - logger.info( - "[%s] Registry initialized status AFTER init: %s", - execution_id, - func_registry_module._registry_initialized, - ) - else: - logger.info("[%s] Registry already initialized, skipping", execution_id) + if func_registry_module.pipeline_source_requires_import_projection( + request_payload.pipeline_code + ): + func_registry_module.initialize_registry() self._cleanup_compiled_artifacts() diff --git a/openhcs/runtime/zmq_execution_server_launcher.py b/openhcs/runtime/zmq_execution_server_launcher.py index 7d2c9ec678..ae4b7c8a40 100644 --- a/openhcs/runtime/zmq_execution_server_launcher.py +++ b/openhcs/runtime/zmq_execution_server_launcher.py @@ -194,10 +194,6 @@ def main( config=config, ) - server.prepare_runtime_capabilities( - lambda status: status_reporter.emit(status.phase, status.message) - ) - status_reporter.emit( EndpointStartupPhase.BINDING_ENDPOINT, "Binding execution server endpoint", diff --git a/tests/unit/test_func_registry_projection.py b/tests/unit/test_func_registry_projection.py index c721f0d91d..140aea754e 100644 --- a/tests/unit/test_func_registry_projection.py +++ b/tests/unit/test_func_registry_projection.py @@ -3,7 +3,7 @@ from __future__ import annotations import sys -from types import SimpleNamespace +from types import ModuleType, SimpleNamespace import pytest @@ -17,6 +17,33 @@ def public_projection_module(self, metadata): return metadata.public_module +@pytest.mark.parametrize( + ("source", "expected"), + ( + ("from openhcs.core.config import PipelineConfig", False), + ("from openhcs.processing.backends.cellprofiler.crop import crop", False), + ("from openhcs.skimage.filters import gaussian", True), + ("from openhcs import skimage", True), + ("importlib.import_module('openhcs.skimage.filters')", True), + ("importlib.import_module(module_name)", True), + ), +) +def test_pipeline_source_requires_only_missing_virtual_import_projection( + source: str, expected: bool +) -> None: + assert func_registry.pipeline_source_requires_import_projection(source) is expected + + +def test_partial_virtual_module_does_not_prove_projection_complete(monkeypatch) -> None: + monkeypatch.setitem( + sys.modules, "openhcs.codex_virtual", ModuleType("openhcs.codex_virtual") + ) + + assert func_registry.pipeline_source_requires_import_projection( + "from openhcs.codex_virtual.filters import noop" + ) + + def test_external_projection_removes_stale_exports_and_modules(monkeypatch) -> None: module_name = "openhcs.codex_external.filters" diff --git a/tests/unit/test_library_registry_discovery.py b/tests/unit/test_library_registry_discovery.py index 569611d8ee..65a7f5262e 100644 --- a/tests/unit/test_library_registry_discovery.py +++ b/tests/unit/test_library_registry_discovery.py @@ -6,8 +6,10 @@ import subprocess import sys import textwrap +import threading +import time from collections.abc import Callable -from concurrent.futures import CancelledError +from concurrent.futures import CancelledError, ThreadPoolExecutor from pathlib import Path from types import SimpleNamespace from typing import Any @@ -16,6 +18,52 @@ import pytest +def test_concurrent_catalog_requests_share_one_metadata_load(monkeypatch) -> None: + from openhcs.processing.backends.lib_registry.registry_service import ( + RegistryService, + ) + + metadata = {"test:example": object()} + load_calls: list[object] = [] + start = threading.Barrier(3) + loading = threading.Event() + release = threading.Event() + + def load_cached(_cls, _instances): + load_calls.append(object()) + loading.set() + assert release.wait(timeout=2) + return metadata + + monkeypatch.setattr(RegistryService, "_metadata_cache", None) + monkeypatch.setattr( + RegistryService, + "_available_registry_instances", + classmethod(lambda _cls: []), + ) + monkeypatch.setattr( + RegistryService, + "_load_valid_persistent_catalog", + classmethod(load_cached), + ) + + def request_metadata(): + start.wait(timeout=2) + return RegistryService.get_all_functions_with_metadata() + + with ThreadPoolExecutor(max_workers=2) as executor: + first = executor.submit(request_metadata) + second = executor.submit(request_metadata) + start.wait(timeout=2) + assert loading.wait(timeout=2) + time.sleep(0.05) + release.set() + assert first.result(timeout=2) is metadata + assert second.result(timeout=2) is metadata + + assert len(load_calls) == 1 + + def test_declared_library_submodules_are_imported_from_the_package_authority( tmp_path: Path, monkeypatch, diff --git a/tests/unit/test_zmq_execution_server.py b/tests/unit/test_zmq_execution_server.py index 080ec4c739..5f8a8b5e5c 100644 --- a/tests/unit/test_zmq_execution_server.py +++ b/tests/unit/test_zmq_execution_server.py @@ -1,6 +1,7 @@ +import sys from pathlib import Path from queue import SimpleQueue -from types import SimpleNamespace +from types import ModuleType, SimpleNamespace import pytest from objectstate import get_current_global_config @@ -188,7 +189,11 @@ def test_zmq_server_reconstructs_pipeline_and_configs_for_artifact_execution( ) -> None: import openhcs.processing.func_registry as func_registry_module - monkeypatch.setattr(func_registry_module, "_registry_initialized", True) + monkeypatch.setattr( + func_registry_module, + "initialize_registry", + lambda: pytest.fail("Direct-import pipeline initialized the full registry"), + ) monkeypatch.setattr( ZMQExecutionServer, "_cleanup_compiled_artifacts", @@ -226,6 +231,59 @@ def test_zmq_server_reconstructs_pipeline_and_configs_for_artifact_execution( assert context.compile_artifact_id == "compile-1" +def test_zmq_server_prepares_virtual_import_before_evaluating_pipeline( + monkeypatch, +) -> None: + import openhcs + import openhcs.processing.func_registry as func_registry_module + + initialized: list[str] = [] + + def initialize_registry() -> None: + initialized.append("registry") + package = ModuleType("openhcs.codex_virtual") + package.__path__ = [] + module = ModuleType("openhcs.codex_virtual.filters") + module.noop = lambda value: value + package.filters = module + monkeypatch.setitem(sys.modules, package.__name__, package) + monkeypatch.setitem(sys.modules, module.__name__, module) + monkeypatch.setattr(openhcs, "codex_virtual", package, raising=False) + + monkeypatch.setattr( + func_registry_module, "initialize_registry", initialize_registry + ) + monkeypatch.setattr( + ZMQExecutionServer, "_cleanup_compiled_artifacts", lambda self: None + ) + monkeypatch.setattr( + ZMQExecutionServer, + "_execute_with_orchestrator", + lambda self, context: context, + ) + request_payload = ZMQExecutionRequestPayload( + identity=ZMQExecutionIdentity(plate_id="/tmp/plate"), + pipeline_code=( + "from openhcs.codex_virtual.filters import noop\n" + "from openhcs.core.config import PipelineConfig\n" + "pipeline_config = PipelineConfig()\n" + "pipeline_steps = []\n" + ), + config_transport=ZMQExecutionConfigTransport( + config_code=( + "from openhcs.core.config import GlobalPipelineConfig\n" + "config = GlobalPipelineConfig()\n" + ), + ), + compile_control=ZMQExecutionCompileControl(), + ) + + context = ZMQExecutionServer()._execute_pipeline("exec-1", request_payload) + + assert initialized == ["registry"] + assert context.pipeline_steps == [] + + def test_zmq_server_forwards_parent_execution_progress_without_worker_claim() -> None: server = object.__new__(ZMQExecutionServer) server._worker_assignments_by_execution = { diff --git a/tests/unit/test_zmq_execution_server_process.py b/tests/unit/test_zmq_execution_server_process.py index 2defd6d859..a3daefb76d 100644 --- a/tests/unit/test_zmq_execution_server_process.py +++ b/tests/unit/test_zmq_execution_server_process.py @@ -85,7 +85,7 @@ def current(cls, *, detached=False): def test_execution_server_launcher_advertises_ready_after_start(monkeypatch) -> None: - """The launcher does not claim readiness before capabilities and sockets start.""" + """The endpoint becomes ready before on-demand catalog preparation.""" events: list[str] = [] @@ -115,7 +115,7 @@ def log_info(message, *_args) -> None: server_runner=serve_forever, ) - assert events == ["construct", "prepare_runtime", "start", "ready", "serve"] + assert events == ["construct", "start", "ready", "serve"] def test_execution_server_launcher_projects_endpoint_overrides_into_config(