Skip to content

Commit f69fffc

Browse files
virtualdFFY00
authored andcommitted
Only launch a single python subprocess/interpreter for performance
Before doing this on my M3 mac: % time pkgconf --list-all pkgconf --list-all 1.10s user 0.35s system 91% cpu 1.589 total After: % time pkgconf --list-all pkgconf --list-all 0.10s user 0.04s system 57% cpu 0.247 total Signed-off-by: Filipe Laíns <[email protected]>
1 parent 142ebae commit f69fffc

File tree

1 file changed

+73
-20
lines changed

1 file changed

+73
-20
lines changed

src/pkgconf/_path_entrypoints.py

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import pathlib
99
import pickle
1010
import sys
11-
import textwrap
1211
import types
1312
import warnings
1413

@@ -67,37 +66,91 @@ def module_path(name: str) -> str:
6766

6867
# Helpers to run the import helpers isolated from the import state of the main process/interpreter
6968

69+
_subinterpreter = None
70+
7071

7172
def run_in_subinterpreter(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
7273
"""Run callable in a subinterpreter — using concurrent.interpreters."""
7374
assert sys.version_info >= (3, 14)
7475

7576
import concurrent.interpreters
7677

77-
with contextlib.closing(concurrent.interpreters.create()) as ip:
78-
return ip.call(fn, *args, **kwargs)
78+
global _subinterpreter
7979

80+
if _subinterpreter is None:
81+
_subinterpreter = concurrent.interpreters.create()
8082

81-
def run_in_subprocess(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
82-
"""Run callable in a subprocess."""
83+
return _subinterpreter.call(fn, *args, **kwargs)
84+
85+
86+
_worker = None
87+
_WORKER_CODE = r"""
88+
import sys, pickle
89+
90+
stdin, stdout = sys.stdin.buffer, sys.stdout.buffer
91+
92+
while True:
93+
try:
94+
length_data = stdin.read(4)
95+
if not length_data:
96+
break
97+
length = int.from_bytes(length_data, "big")
98+
payload = stdin.read(length)
99+
fn, args, kwargs = pickle.loads(payload)
100+
try:
101+
result = fn(*args, **kwargs)
102+
data = pickle.dumps((True, result))
103+
except Exception as e:
104+
data = pickle.dumps((False, e))
105+
stdout.write(len(data).to_bytes(4, "big"))
106+
stdout.write(data)
107+
stdout.flush()
108+
except Exception:
109+
break
110+
"""
111+
112+
113+
def _make_worker():
114+
"""Start worker subprocess and assign callable to _worker."""
115+
global _worker
83116
import subprocess
84117

85-
pickled_call_tuple = pickle.dumps((fn, args, kwargs))
86-
process_cmd = textwrap.dedent(f"""
87-
import pickle, sys
118+
proc = subprocess.Popen(
119+
[sys.executable, '-u', '-c', _WORKER_CODE],
120+
stdin=subprocess.PIPE,
121+
stdout=subprocess.PIPE,
122+
)
88123

89-
fn, args, kwargs = pickle.loads({pickled_call_tuple})
90-
value = fn(*args, **kwargs)
91-
sys.stdout.buffer.write(
92-
pickle.dumps(value)
93-
)
94-
""")
95-
pickled_value = subprocess.run(
96-
[sys.executable, '-c', process_cmd],
97-
check=True,
98-
capture_output=True,
99-
).stdout
100-
return pickle.loads(pickled_value)
124+
def _worker_fn(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
125+
payload = pickle.dumps((fn, args, kwargs))
126+
length = len(payload).to_bytes(4, 'big')
127+
128+
proc.stdin.write(length)
129+
proc.stdin.write(payload)
130+
proc.stdin.flush()
131+
132+
length_data = proc.stdout.read(4)
133+
if not length_data:
134+
msg = 'Subprocess died'
135+
raise RuntimeError(msg)
136+
resp_length = int.from_bytes(length_data, 'big')
137+
data = proc.stdout.read(resp_length)
138+
ok, value = pickle.loads(data)
139+
140+
if ok:
141+
return value
142+
else:
143+
raise value
144+
145+
_worker = _worker_fn
146+
147+
148+
def run_in_subprocess(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
149+
"""Run callable in a subprocess."""
150+
if _worker is None:
151+
_make_worker()
152+
153+
return _worker(fn, *args, **kwargs)
101154

102155

103156
def run_in_isolated_context(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:

0 commit comments

Comments
 (0)