1
1
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03a_parallel.ipynb.
2
2
3
3
# %% auto 0
4
- __all__ = ['threaded' , 'startthread' , 'parallelable' , 'ThreadPoolExecutor' , 'ProcessPoolExecutor' , 'parallel' , 'add_one ' ,
5
- 'run_procs' , 'parallel_gen' ]
4
+ __all__ = ['threaded' , 'startthread' , 'parallelable' , 'ThreadPoolExecutor' , 'ProcessPoolExecutor' , 'NoDaemonProcess ' ,
5
+ 'ProcessPool' , 'ThreadPool' , 'parallel' , 'add_one' , ' run_procs' , 'parallel_gen' ]
6
6
7
7
# %% ../nbs/03a_parallel.ipynb 1
8
8
from .imports import *
11
11
from .meta import *
12
12
from .xtras import *
13
13
from functools import wraps
14
-
15
14
import concurrent .futures ,time
16
- from multiprocessing import Process ,Queue ,Manager ,set_start_method ,get_all_start_methods ,get_context
15
+ from multiprocessing import pool , Process ,Queue ,Manager ,set_start_method ,get_all_start_methods ,get_context
17
16
from threading import Thread
18
17
try :
19
18
if sys .platform == 'darwin' and IN_NOTEBOOK : set_start_method ("fork" )
20
19
except : pass
21
20
22
- # %% ../nbs/03a_parallel.ipynb 4
21
+ # %% ../nbs/03a_parallel.ipynb 5
23
22
def threaded (f ):
24
23
"Run `f` in a thread, and returns the thread"
25
24
@wraps (f )
@@ -29,12 +28,12 @@ def _f(*args, **kwargs):
29
28
return res
30
29
return _f
31
30
32
- # %% ../nbs/03a_parallel.ipynb 6
31
+ # %% ../nbs/03a_parallel.ipynb 7
33
32
def startthread (f ):
34
33
"Like `threaded`, but start thread immediately"
35
34
threaded (f )()
36
35
37
- # %% ../nbs/03a_parallel.ipynb 8
36
+ # %% ../nbs/03a_parallel.ipynb 9
38
37
def _call (lock , pause , n , g , item ):
39
38
l = False
40
39
if pause :
@@ -45,7 +44,7 @@ def _call(lock, pause, n, g, item):
45
44
if l : lock .release ()
46
45
return g (item )
47
46
48
- # %% ../nbs/03a_parallel.ipynb 9
47
+ # %% ../nbs/03a_parallel.ipynb 10
49
48
def parallelable (param_name , num_workers , f = None ):
50
49
f_in_main = f == None or sys .modules [f .__module__ ].__name__ == "__main__"
51
50
if sys .platform == "win32" and IN_NOTEBOOK and num_workers > 0 and f_in_main :
@@ -54,7 +53,7 @@ def parallelable(param_name, num_workers, f=None):
54
53
return False
55
54
return True
56
55
57
- # %% ../nbs/03a_parallel.ipynb 10
56
+ # %% ../nbs/03a_parallel.ipynb 11
58
57
class ThreadPoolExecutor (concurrent .futures .ThreadPoolExecutor ):
59
58
"Same as Python's ThreadPoolExecutor, except can pass `max_workers==0` for serial execution"
60
59
def __init__ (self , max_workers = defaults .cpus , on_exc = print , pause = 0 , ** kwargs ):
@@ -72,7 +71,7 @@ def map(self, f, items, *args, timeout=None, chunksize=1, **kwargs):
72
71
try : return super ().map (_g , items , timeout = timeout , chunksize = chunksize )
73
72
except Exception as e : self .on_exc (e )
74
73
75
- # %% ../nbs/03a_parallel.ipynb 12
74
+ # %% ../nbs/03a_parallel.ipynb 13
76
75
@delegates ()
77
76
class ProcessPoolExecutor (concurrent .futures .ProcessPoolExecutor ):
78
77
"Same as Python's ProcessPoolExecutor, except can pass `max_workers==0` for serial execution"
@@ -95,49 +94,101 @@ def map(self, f, items, *args, timeout=None, chunksize=1, **kwargs):
95
94
try : return super ().map (_g , items , timeout = timeout , chunksize = chunksize )
96
95
except Exception as e : self .on_exc (e )
97
96
98
- # %% ../nbs/03a_parallel.ipynb 14
97
+ # %% ../nbs/03a_parallel.ipynb 15
98
+ class NoDaemonProcess (Process ):
99
+ # See https://stackoverflow.com/questions/6974695/python-process-pool-non-daemonic
100
+ @property
101
+ def daemon (self ):
102
+ return False
103
+ @daemon .setter
104
+ def daemon (self , value ):
105
+ pass
106
+
107
+ # %% ../nbs/03a_parallel.ipynb 16
108
+ @delegates ()
109
+ class ProcessPool (pool .Pool ):
110
+ "Same as Python's Pool, except can pass `max_workers==0` for serial execution"
111
+ def __init__ (self , max_workers = defaults .cpus , on_exc = print , pause = 0 , daemonic = False , ** kwargs ):
112
+ if max_workers is None : max_workers = defaults .cpus
113
+ store_attr ()
114
+ self .not_parallel = max_workers == 0
115
+ if self .not_parallel : max_workers = 1
116
+ if not daemonic :
117
+ class NoDaemonContext (type (kwargs .get ('context' , get_context ()))):
118
+ Process = NoDaemonProcess
119
+ kwargs ['context' ] = NoDaemonContext ()
120
+ super ().__init__ (max_workers , ** kwargs )
121
+
122
+ def map (self , f , items , * args , timeout = None , chunksize = 1 , ** kwargs ):
123
+ assert timeout is None , "timeout is not supported by ProcessPool, use ProcessPoolExecutor instead"
124
+ if not parallelable ('max_workers' , self .max_workers , f ): self .max_workers = 0
125
+ self .not_parallel = self .max_workers == 0
126
+ if self .not_parallel : self .max_workers = 1
127
+
128
+ if self .not_parallel == False : self .lock = Manager ().Lock ()
129
+ g = partial (f , * args , ** kwargs )
130
+ if self .not_parallel : return map (g , items )
131
+ _g = partial (_call , self .lock , self .pause , self .max_workers , g )
132
+ try : return super ().map (_g , items , chunksize = chunksize )
133
+ except Exception as e : self .on_exc (e )
134
+
135
+ # %% ../nbs/03a_parallel.ipynb 17
136
+ @delegates ()
137
+ class ThreadPool ():
138
+ # If you have a need for a ThreadPool, please open an issue.
139
+ def __init__ (self , * args , ** kwargs ):
140
+ raise NotImplementedError ("`ThreadPool` is not implemented" )
141
+
142
+ # %% ../nbs/03a_parallel.ipynb 18
99
143
try : from fastprogress import progress_bar
100
144
except : progress_bar = None
101
145
102
- # %% ../nbs/03a_parallel.ipynb 15
146
+ # %% ../nbs/03a_parallel.ipynb 19
103
147
def parallel (f , items , * args , n_workers = defaults .cpus , total = None , progress = None , pause = 0 ,
104
- method = None , threadpool = False , timeout = None , chunksize = 1 , ** kwargs ):
148
+ method = None , threadpool = False , timeout = None , chunksize = 1 ,
149
+ executor = True , maxtasksperchild = None , ** kwargs ):
105
150
"Applies `func` in parallel to `items`, using `n_workers`"
106
151
kwpool = {}
107
- if threadpool : pool = ThreadPoolExecutor
152
+ if threadpool : pool = ThreadPoolExecutor if executor else ThreadPool
108
153
else :
154
+ pool = ProcessPoolExecutor if executor else ProcessPool
109
155
if not method and sys .platform == 'darwin' : method = 'fork'
110
- if method : kwpool ['mp_context' ] = get_context (method )
111
- pool = ProcessPoolExecutor
156
+ if method :
157
+ if executor : kwpool ['mp_context' ] = get_context (method )
158
+ else : kwpool ['context' ] = get_context (method )
159
+
160
+ if maxtasksperchild :
161
+ assert pool == ProcessPool , "`maxtasksperchild` is only supported by ProcessPool"
162
+ kwpool ['maxtasksperchild' ] = maxtasksperchild
112
163
with pool (n_workers , pause = pause , ** kwpool ) as ex :
113
164
r = ex .map (f ,items , * args , timeout = timeout , chunksize = chunksize , ** kwargs )
114
165
if progress and progress_bar :
115
166
if total is None : total = len (items )
116
167
r = progress_bar (r , total = total , leave = False )
117
168
return L (r )
118
169
119
- # %% ../nbs/03a_parallel.ipynb 16
170
+ # %% ../nbs/03a_parallel.ipynb 20
120
171
def add_one (x , a = 1 ):
121
172
# this import is necessary for multiprocessing in notebook on windows
122
173
import random
123
174
time .sleep (random .random ()/ 80 )
124
175
return x + a
125
176
126
- # %% ../nbs/03a_parallel.ipynb 22
177
+ # %% ../nbs/03a_parallel.ipynb 26
127
178
def run_procs (f , f_done , args ):
128
179
"Call `f` for each item in `args` in parallel, yielding `f_done`"
129
180
processes = L (args ).map (Process , args = arg0 , target = f )
130
181
for o in processes : o .start ()
131
182
yield from f_done ()
132
183
processes .map (Self .join ())
133
184
134
- # %% ../nbs/03a_parallel.ipynb 23
185
+ # %% ../nbs/03a_parallel.ipynb 27
135
186
def _f_pg (obj , queue , batch , start_idx ):
136
187
for i ,b in enumerate (obj (batch )): queue .put ((start_idx + i ,b ))
137
188
138
189
def _done_pg (queue , items ): return (queue .get () for _ in items )
139
190
140
- # %% ../nbs/03a_parallel.ipynb 24
191
+ # %% ../nbs/03a_parallel.ipynb 28
141
192
def parallel_gen (cls , items , n_workers = defaults .cpus , ** kwargs ):
142
193
"Instantiate `cls` in `n_workers` procs & call each on a subset of `items` in parallel."
143
194
if not parallelable ('n_workers' , n_workers ): n_workers = 0
0 commit comments