Skip to content

perf: std::env::var("RUSTFOIL_*_DEBUG") checks in hot BL loops serialize the rayon workers #19

Description

@aeronauty-flexcompute

Summary

The debug-flag guards in the viscous solver's inner loops call std::env::var(...) on every iteration. On macOS (and glibc Linux under some conditions), getenv takes a process-global lock (an os_unfair_lock on Darwin), so when a batch polar sweep fans out across the rayon pool, the workers spend a large share of their time contending on that lock instead of solving. Observed from flexfoil 1.1.6 in a Python API process: a batch sweep saturating ~12 cores (~1240% CPU) delivered nowhere near 12x single-thread throughput; profiling the host process showed heavy contention in getenv under solve_body_oper_point.

Where

std::env::var("RUSTFOIL_*_DEBUG").is_ok() guards inside per-station / per-Newton-iteration code, e.g.:

  • crates/rustfoil-xfoil/src/march.rs:713 (RUSTFOIL_WAKE_MARCH_DEBUG), :932 / :1275 (RUSTFOIL_TRCHEK_DEBUG), :1110 (RUSTFOIL_WAKE_ITER_DEBUG), :1127 (RUSTFOIL_TAIL_ITER_DEBUG)
  • crates/rustfoil-xfoil/src/update.rs:120 (RUSTFOIL_UPDATE_DEBUG)
  • similar guards in solve.rs, oper.rs, assembly.rs, state_ops.rs, and rustfoil-solver/src/viscous/{viscal,forces}.rs

These run per BL station per Newton iteration per operating point - easily 10^5-10^6 getenv calls per polar, times N rayon workers hammering the same lock.

Suggested fix

Read each flag once into a std::sync::LazyLock<bool> (or OnceLock) at first use and branch on the cached bool:

static UPDATE_DEBUG: LazyLock<bool> =
    LazyLock::new(|| std::env::var("RUSTFOIL_UPDATE_DEBUG").is_ok());

Debug flags don't need to be re-read mid-run; process-start semantics are fine (and are already the effective semantics for anyone using them). A small debug_flags module holding all the flags would keep it tidy.

Impact

This is why capping RAYON_NUM_THREADS barely hurts sweep throughput today - the extra workers were mostly spinning on the getenv lock. Fixing this should make batch sweeps actually scale with the pool size.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions