joblib backend="multiprocessing" repeats NumPy draws on every call; loky does not
Measured on Linux, CPython 3.12.3, NumPy 2.5.4, joblib 1.6.0, Parallel(n_jobs=4), one sleeping task per worker, after np.random.seed(0) in the parent. backend="loky" (the default) gave 4/4 distinct np.random.random() values, also on a second call that reused the workers, because loky starts fresh interpreters. backend="multiprocessing" gave 1/4 on both calls: each Parallel call forks a new pool from the same parent state. So the draws repeat across workers and also across calls. When you audit code for duplicated randomness, joblib calls with the default backend can be cleared quickly. Calls that set backend="multiprocessing" (some libraries make it a config default) need a closer look.
Confirmed on CPython 3.12.16, NumPy 2.5.4 and joblib 1.6.0 (n_jobs=4, a 0.3 s sleep per task, np.random.seed(0) in the parent). loky gave 4 of 4. backend='multiprocessing' gave 1 of 4, and a second, separate Parallel(...)(...) call returned exactly the first call's value again. One qualification on 'across calls': if the calls share a pool through 'with Parallel(n_jobs=4, backend="multiprocessing") as par:', the second call is still 1 of 4 across workers, but it does not repeat the first call, because the workers' streams have advanced. Only a fresh Parallel object re-forks from the unchanged parent state.
Which scikit-learn paths this hits, with sklearn 1.9.1 / joblib 1.6.0 / NumPy 2.5.4: estimators with random_state=None that resolve it inside the worker. cross_validate(est, X, y, cv=[(tr, te)]*4) under joblib.parallel_backend('multiprocessing', n_jobs=4) gave 1 of 4 distinct test scores for DummyClassifier(strategy='uniform'), MLPClassifier and SGDClassifier, on 3.12.3 (fork) and on 3.14.10 (default forkserver; the script imports sklearn at top level, so numpy.random is loaded in the server). loky gave 4 of 4. Not affected: RandomForestClassifier(n_jobs=4) and BaggingClassifier(n_jobs=4) under the same backend gave 8 of 8 distinct trees and bootstrap samples, because the per-estimator seeds are drawn in the parent before dispatch. So GridSearchCV/cross_validate candidates silently share one random init per fork, while ensembles stay fine.