Skip to content

libpython: Speed up grass.lib import by registering $GISBASE/lib - #7605

Merged
petrasovaa merged 2 commits into
OSGeo:mainfrom
petrasovaa:python-faster-lib-loading
Jun 30, 2026
Merged

petrasovaa merged 2 commits into
OSGeo:mainfrom
petrasovaa:python-faster-lib-loading

Conversation

@petrasovaa

Copy link
Copy Markdown
Contributor

Context is #7585 (import tgis takes too long). We found out that for Mac it is much faster, which triggered this solution. Claude debugged it:

Root cause

GRASS libraries live in $GISBASE/lib, which the ctypes loader does not search before falling back to ctypes.util.find_library(). On Linux that fallback shells out to ldconfig/gcc/ld/objdump once per library (~90 subprocesses for the grass.temporal chain). macOS is unaffected because its find_library() spawns nothing.

Fix

Register $GISBASE/lib with the loader in grass/lib/__init__.py so libraries resolve by absolute path and the fallback is never reached. Gated on GISBASE, so out-of-session imports are unchanged.

Results

Cold import grass.temporal on Linux: ~2560 ms / 90 subprocesses → ~200 ms / 0. ~13x faster, matching macOS.

@nilason I would appreciate your review on this. I tested the fix, seems fine, but I don't know if there are any unintended consequences.

…/grass/grass_main/dist.x86_64-pc-linux-gnu/lib
@petrasovaa
petrasovaa requested a review from nilason June 24, 2026 15:37
@petrasovaa petrasovaa linked an issue Jun 24, 2026 that may be closed by this pull request
@github-actions github-actions Bot added Python Related code is in Python libraries labels Jun 24, 2026
nilason
nilason previously approved these changes Jun 27, 2026

@nilason nilason left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see how this would break something, so let's try it out.

@wenzeslaus wenzeslaus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The performance gain is crucial and the code placement is right. I did some AI/search to understand more here, specifically why this is even needed when we set LD_LIBRARY_PATH anyway (or even when we would do rpath): The mechanism this is influencing is the ctypesgen loading mechanism, so this works well given how ctypesgen is implemented to deal with cross-platform issues. The standard Linux (or any OS) library loading comes later in the process.

In case anyone is interested, here is an AI summary of my conversation with Google search AI/Gemini:

When LD_LIBRARY_PATH is already correctly set before Python starts, the Linux kernel loader has no trouble resolving deep dependencies. The core issue is that ctypes.util.find_library() prioritizes running /sbin/ldconfig -p, a system tool that completely ignores LD_LIBRARY_PATH and only scans standard system directories. When ldconfig fails to find your GRASS libraries in the system cache, Python enters a slow fallback routine that spawns heavy external shell subprocesses (gcc and objdump) to locate the file, creating a massive performance bottleneck for every single library imported.

Your add_library_search_dirs addition acts as a direct shortcut that short-circuits this broken Python loop using basic, instantaneous file-presence checks. By finding the entry-point library instantly, it skips the expensive find_library() routine entirely and hands ctypes.CDLL() an absolute path (like /path/to/grass/lib/libgrass_gis.so). Because your LD_LIBRARY_PATH is already active in the background, the Linux kernel loader seamlessly takes over from there, resolving all the deeper nested dependencies perfectly and instantly.

@petrasovaa
petrasovaa merged commit 000590f into OSGeo:main Jun 30, 2026
26 checks passed
@github-actions github-actions Bot added this to the 8.6.0 milestone Jun 30, 2026
ninsbl pushed a commit to ninsbl/grass that referenced this pull request Jul 7, 2026
…eo#7605)

Register $GISBASE/lib (where the GRASS shared libraries are installed) with
the ctypes loader before any wrapper module loads its library. The loader
searches these directories first, so it finds the libraries by absolute path
and never reaches its ctypes.util.find_library() fallback. That fallback is
expensive on Linux, where find_library() spawns the toolchain (ldconfig, gcc,
ld, objdump) once per library; for the chain pulled in by grass.temporal that
was about 90 subprocesses and the bulk of the import time. The libraries are
still resolvable through the runtime linker path, so this is a startup
optimization, not a correctness fix; outside a GRASS session GISBASE may be
unset, in which case the loader keeps its original behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libraries Python Related code is in Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] import tgis takes too long

3 participants