Skip to content

Commit 71b9bd6

Browse files
ampagentIsaac Miller
authored andcommitted
fix(gepa): distribute candidate thread remainder
Amp-Thread-ID: https://ampcode.com/threads/T-019ffe40-c240-70bb-8cb2-db33146ed8e6 Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
1 parent 5980cdd commit 71b9bd6

2 files changed

Lines changed: 16 additions & 6 deletions

File tree

‎dspy/teleprompt/gepa/gepa_utils.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,21 +273,25 @@ def evaluate(self, batch, candidate, capture_traces=False, num_threads=None):
273273

274274
def batch_evaluate(self, items, *, capture_traces=True):
275275
total_threads = self.num_threads or dspy.settings.num_threads
276-
candidate_threads = min(len(items), total_threads) or 1
277-
threads_per_candidate = max(1, total_threads // candidate_threads)
276+
candidate_workers = min(len(items), total_threads) or 1
277+
threads_per_candidate, extra_threads = divmod(total_threads, candidate_workers)
278278

279279
def evaluate(item):
280-
context, (candidate, batch) = item
280+
context, (candidate, batch), num_threads = item
281281
return context.run(
282282
self.evaluate,
283283
batch,
284284
candidate,
285285
capture_traces=capture_traces,
286-
num_threads=threads_per_candidate,
286+
num_threads=num_threads,
287287
)
288288

289-
with ThreadPoolExecutor(max_workers=candidate_threads) as executor:
290-
return list(executor.map(evaluate, [(copy_context(), item) for item in items]))
289+
work = [
290+
(copy_context(), item, threads_per_candidate + (index < extra_threads))
291+
for index, item in enumerate(items)
292+
]
293+
with ThreadPoolExecutor(max_workers=candidate_workers) as executor:
294+
return list(executor.map(evaluate, work))
291295

292296
def get_adapter_state(self) -> dict[str, Any]:
293297
return {"rng_state": self.rng.getstate()}

‎tests/teleprompt/test_gepa.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,3 +695,9 @@ def evaluate(batch, candidate, capture_traces=False, num_threads=None):
695695
adapter.batch_evaluate([("first", [])], capture_traces=False)
696696
assert trace_modes == [True, True, False]
697697
assert thread_budgets == [2, 2, 4]
698+
699+
adapter.num_threads = 8
700+
adapter.batch_evaluate([("first", []), ("second", []), ("third", [])], capture_traces=False)
701+
assert trace_modes == [True, True, False, False, False, False]
702+
assert sorted(thread_budgets[-3:]) == [2, 3, 3]
703+
assert sum(thread_budgets[-3:]) == 8

0 commit comments

Comments
 (0)