Skip to content

Xfoil hard dependency costs ~2.45 s of __init__ compilation on every using VortexStepMethod #274

Description

@ufechner7

Summary

Xfoil is a hard dependency, and Xfoil.__init__() costs ~2.45 s of fresh compilation in every Julia process that does using VortexStepMethod. Nothing on the aerodynamic solve path needs it — it is only reached through XFoilSolver. Making it a weak dependency would remove that cost for every user who does not generate polars with XFOIL.

Measurement

Julia 1.12.7, VortexStepMethod v4.3.1, measured via @time_imports using V3Kite (V3Kite depends on VSM), with --startup-file=no:

   221.6 ms  IntelOpenMP_jll
   733.7 ms  ModelingToolkitBase
   502.9 ms  TimeZones
           ┌ 2452.2 ms Xfoil.__init__() 99.99% compilation time
  2454.3 ms  Xfoil 99.90% compilation time
    92.2 ms  VortexStepMethod

Total for that using is ~10.3 s, so Xfoil alone is ~24% of it — the single largest entry, larger than the entire ModelingToolkit/Symbolics stack's individual contributions.

Cause

Xfoil.__init__ calls get_globals() and get_globals_cs() (Xfoil.jl:19-24), which build large NamedTuples wrapping the Fortran globals. Code reachable only from __init__ is not inferred while the module body is precompiled, so it is compiled from scratch on every load and cannot be cached in the pkgimage. This is upstream behaviour and not something VSM can fix directly — but VSM can avoid paying for it.

Why an extension fits here

In v4.3.1 the Xfoil surface is already narrow and already abstracted:

  • using Xfoil appears once, in src/airfoil_aero/AirfoilAero.jl:8
  • all call sites are in src/airfoil_aero/airfoil_solvers/xfoil_solver.jl (set_coordinates, pane, solve_alpha, cpdump, bldump)
  • XFoilSolver <: AbstractAirfoilSolver sits alongside neuralfoil_solver.jl behind the same interface, so users on the NeuralFoil path already never touch XFOIL at runtime

Proposed change

Move Xfoil to [weakdeps] and xfoil_solver.jl into a VortexStepMethodXfoilExt, mirroring the existing VortexStepMethodMakieExt. XFoilSolver would become available after using Xfoil; users who generate polars already have a reason to load it explicitly, and everyone else saves ~2.45 s per process.

If keeping XFoilSolver unconditionally exported matters, a stub method that errors with "run using Xfoil first" (with the extension providing the real, more specific method) keeps the name discoverable.

Alternative

An upstream PR to Xfoil.jl adding a PrecompileTools.@compile_workload that calls get_globals()/get_globals_cs() would cache those specializations in Xfoil's own pkgimage and fix this for all downstream users. That is the better global fix, but it is out of your hands and does not help until it lands and is released.

Context

Reported from the OpenSourceAWE stack — using V3Kite takes 12.3 s in a Revise-enabled REPL, and Xfoil plus a separate HDF5 invalidation account for roughly half of it. The HDF5 half has been fixed in V3Kite by the same weakdep treatment.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions