1515# ################################################################################
1616
1717import importlib
18- import shutil
1918import string
2019import sys
2120import tempfile
21+ from contextlib import contextmanager
2222
2323try :
2424 from cffi import FFI
6161# We assume the 0-th argument supports either DLPack or CUDA Array Interface (both
6262# of which are supported by StridedMemoryView).
6363@args_viewable_as_strided_memory ((0 ,))
64- def my_func (arr ):
65- global cpu_func
66- global cpu_prog
64+ def my_func (arr , cpu_prog , cpu_func ):
6765 # Create a memory view over arr (assumed to be a 1D array of int32). The stream
6866 # ordering is taken care of, so that arr can be safely accessed on our work
6967 # stream (ordered after a data stream on which arr is potentially prepared).
@@ -79,8 +77,7 @@ def my_func(arr):
7977 cpu_func (cpu_prog .cast ("int*" , view .ptr ), size )
8078
8179
82- def run ():
83- global my_func , cpu_func , cpu_prog
80+ def _create_cpu_program ():
8481 # Here is a concrete (very naive!) implementation on CPU:
8582 cpu_code = string .Template (r"""
8683 extern "C"
@@ -101,34 +98,37 @@ def run():
10198 source_extension = ".cpp" ,
10299 extra_compile_args = ["-std=c++11" ],
103100 )
104- temp_dir = tempfile .mkdtemp ()
105- cpu_func = None
101+ return cpu_prog
102+
103+
104+ @contextmanager
105+ def _compiled_cpu_func (cpu_prog , temp_dir ):
106106 saved_sys_path = sys .path .copy ()
107107 try :
108108 cpu_prog .compile (tmpdir = temp_dir )
109-
110109 sys .path .append (temp_dir )
111110 cpu_func = getattr (importlib .import_module ("_cpu_obj.lib" ), func_name )
111+ yield cpu_func
112+ finally :
113+ sys .path = saved_sys_path
114+ # Ensure cffi modules are unloadable before removing the temp build dir.
115+ sys .modules .pop ("_cpu_obj.lib" , None )
116+ sys .modules .pop ("_cpu_obj" , None )
112117
113- # Create input array on CPU
114- arr_cpu = np .zeros (1024 , dtype = np .int32 )
115- print (f"before: { arr_cpu [:10 ]= } " )
116118
117- # Run the workload
118- my_func (arr_cpu )
119+ def _run_example (cpu_prog , cpu_func ):
120+ arr_cpu = np .zeros (1024 , dtype = np .int32 )
121+ print (f"before: { arr_cpu [:10 ]= } " , file = sys .stderr )
122+ my_func (arr_cpu , cpu_prog , cpu_func )
123+ print (f"after: { arr_cpu [:10 ]= } " , file = sys .stderr )
124+ assert np .allclose (arr_cpu , np .arange (1024 , dtype = np .int32 ))
119125
120- # Check the result
121- print (f"after: { arr_cpu [:10 ]= } " )
122- assert np .allclose (arr_cpu , np .arange (1024 , dtype = np .int32 ))
123- finally :
124- sys .path = saved_sys_path
125- # to allow FFI module to unload, we delete references to
126- # to cpu_func
127- if cpu_func is not None :
128- del cpu_func
129- del cpu_prog , my_func
130- # clean up temp directory
131- shutil .rmtree (temp_dir )
126+
127+ def run ():
128+ cpu_prog = _create_cpu_program ()
129+ with tempfile .TemporaryDirectory () as temp_dir :
130+ with _compiled_cpu_func (cpu_prog , temp_dir ) as cpu_func :
131+ _run_example (cpu_prog , cpu_func )
132132
133133
134134if __name__ == "__main__" :
0 commit comments