Skip to content

Conversation

@shingjan
Copy link

@shingjan shingjan commented Jul 16, 2021

This PR intends to replace the use of multiprocessing.Pool to popen_pool.PopenPoolExecutor in auto_scheduler.

@tkonolige
Copy link
Contributor

What is the purpose of switching from ThreadPool to PopenPoolExecutor?

@junrushao
Copy link
Member

PopenPool is intended to replace multiprocessing (not multithreading), so just curious, do we have specific reasons here to replace the ThreadPool here?

@vinx13
Copy link
Member

vinx13 commented Jul 17, 2021

We can keep the thread pool here, and only replace multiprocessing.Pool

@shingjan
Copy link
Author

By far those are all uses of multiprocessing.pool in this repo that I can find, is there any places that this change should also apply to? @junrushao1994 @vinx13 @tkonolige

@shingjan shingjan requested a review from vinx13 July 23, 2021 00:15
@vinx13
Copy link
Member

vinx13 commented Jul 23, 2021

please fix ci error

@shingjan
Copy link
Author

auto_scheduler need to be fixed:
File "/workspace/python/tvm/exec/popen_worker.py", line 77, in main
fn, args, kwargs, timeout = cloudpickle.loads(reader.read(bytes_size))
ModuleNotFoundError: No module named 'test_auto_scheduler_common'

@shingjan
Copy link
Author

Exception ignored in: <function XGBoostCostModel.del at 0x7f93082e7b00>
Traceback (most recent call last):
File "/Users/yjshi/Repo/tvm/python/tvm/autotvm/tuner/xgboost_cost_model.py", line 353, in del
File "/Users/yjshi/Repo/tvm/python/tvm/autotvm/tuner/xgboost_cost_model.py", line 168, in _close_pool
AttributeError: 'XGBoostCostModel' object has no attribute 'pool'

@vinx13
Copy link
Member

vinx13 commented Jul 30, 2021

auto_scheduler need to be fixed:
File "/workspace/python/tvm/exec/popen_worker.py", line 77, in main
fn, args, kwargs, timeout = cloudpickle.loads(reader.read(bytes_size))
ModuleNotFoundError: No module named 'test_auto_scheduler_common'

Discussed with @shingjan , we should avoid importing non-package file in the test directory when writing tests, these common code should be put in tvm.testing or tvm.auto_scheduler.testing. Otherwise subprocesses created through popenpool will not be able to import them as they are not in PYTHONPATH cc @tqchen @junrushao1994

@tqchen
Copy link
Member

tqchen commented Jul 30, 2021

tvm.testing sounds right

@vinx13
Copy link
Member

vinx13 commented Aug 11, 2021

would be great if @comaniac @jcf94 @merrymercy could also take a look

@junrushao
Copy link
Member

Thanks for all your effort in this refactoring 🙏

Copy link
Contributor

@tkonolige tkonolige left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good to me. I've listed a couple of little things to change. Thanks!

Copy link
Member

@junrushao junrushao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really exciting work! I don't have other comments :-)

@junrushao junrushao merged commit 4dd7f68 into apache:main Aug 12, 2021
@junrushao junrushao changed the title [TIR] Use PopenPool instead of multiprocessing.pool [AutoScheduler] Use PopenPool instead of multiprocessing.pool Aug 13, 2021
@Wanger-SJTU
Copy link
Contributor

Wanger-SJTU commented Sep 2, 2021

@tqchen @shingjan @tkonolige @junrushao1994 @vinx13 this pr make builder=auto_scheduler.LocalBuilder(build_func="ndk" if use_ndk else "default") fail. No matter what build_func specific, the build_func will fall back to default.
and with this pr, the demo code tutorials/auto_scheduler/tune_network_arm.py run failed with use_ndk=true

python  tune_network_arm.py
default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) c
Get model...
Extract tasks...
Begin tuning...
ndk
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) c
Get devices for measurement successfully!
> /home/framework/tvm/tutorials/auto_scheduler/tune_network_arm.py(326)tune_and_evaluate()
-> tuner.tune(tune_option)
(Pdb) c
----------------------------------------------------------------------
------------------------------  [ Task Scheduler ]
----------------------------------------------------------------------
|  ID  | Latency (ms) | Speed (GFLOPS) | Trials |
-------------------------------------------------
|    0 |            - |              - |      0 |
|    1 |            - |              - |      0 |
|    2 |            - |              - |      0 |
|    3 |            - |              - |      0 |
|    4 |            - |              - |      0 |
|    5 |            - |              - |      0 |
|    6 |            - |              - |      0 |
|    7 |            - |              - |      0 |
|    8 |            - |              - |      0 |
|    9 |            - |              - |      0 |
|   10 |            - |              - |      0 |
|   11 |            - |              - |      0 |
|   12 |            - |              - |      0 |
|   13 |            - |              - |      0 |
|   14 |            - |              - |      0 |
|   15 |            - |              - |      0 |
|   16 |            - |              - |      0 |
|   17 |            - |              - |      0 |
|   18 |            - |              - |      0 |
|   19 |            - |              - |      0 |
|   20 |            - |              - |      0 |
|   21 |            - |              - |      0 |
-------------------------------------------------
Estimated total latency: - ms   Trials: 0       Used time : 1 s Next ID: 0
----------------------------------------------------------------------
------------------------------  [ Search ]
----------------------------------------------------------------------
Generate Sketches               #s: 3
Sample Initial Population       #s: 1658        fail_ct: 194    Time elapsed: 0.97
GA Iter: 0      Max score: 0.9999       Min score: 0.9896       #Pop: 18        #M+: 0  #M-: 0
GA Iter: 4      Max score: 1.0000       Min score: 0.9985       #Pop: 18        #M+: 1374       #M-: 76
EvolutionarySearch              #s: 18  Time elapsed: 3.82
----------------------------------------------------------------------
------------------------------  [ Measure ]
----------------------------------------------------------------------
Get 9 programs to measure:
default
default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":default

> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":(Pdb) 
(Pdb) > /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) default
default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) > /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) 
Traceback (most recent call last):
  File "/home/anaconda3/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/anaconda3/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/framework/tvm/python/tvm/exec/popen_worker.py", line 105, in <module>
    main()
  File "/home/framework/tvm/python/tvm/exec/popen_worker.py", line 77, in main
    fn, args, kwargs, timeout = cloudpickle.loads(reader.read(bytes_size))
  File "/home/framework/tvm/python/tvm/auto_scheduler/__init__.py", line 21, in <module>
    from . import dispatcher
  File "/home/framework/tvm/python/tvm/auto_scheduler/dispatcher.py", line 37, in <module>
    from .search_task import SearchTask, TuningOptions
  File "/home/framework/tvm/python/tvm/auto_scheduler/search_task.py", line 635, in <module>
    def auto_schedule(task, search_policy=None, tuning_options=TuningOptions()):
  File "/home/framework/tvm/python/tvm/auto_scheduler/search_task.py", line 184, in __init__
    builder = LocalBuilder()
  File "/home/framework/tvm/python/tvm/auto_scheduler/measure.py", line 341, in __init__
    if build_func == "default":
  File "/home/framework/tvm/python/tvm/auto_scheduler/measure.py", line 341, in __init__
    if build_func == "default":
  File "/home/anaconda3/lib/python3.8/bdb.py", line 88, in trace_dispatch
    return self.dispatch_line(frame)
  File "/home/anaconda3/lib/python3.8/bdb.py", line 113, in dispatch_line
    if self.quitting: raise BdbQuit
bdb.BdbQuit
default
> /home/framework/tvm/python/tvm/auto_scheduler/measure.py(341)__init__()
-> if build_func == "default":
(Pdb) 

@echuraev
Copy link
Contributor

echuraev commented Sep 2, 2021

@Wanger-SJTU I also met this problem. The problem is in new mechanism of creating processes and in serializing global object such as BuildFunc. I'm working on a fix for this problem.

@junrushao
Copy link
Member

@shingjan @vinx13 would you guys take a closer look at the ndk issue as well?

@echuraev
Copy link
Contributor

echuraev commented Sep 2, 2021

@shingjan @vinx13 would you guys take a closer look at the ndk issue as well?

This problem is not only related to the ndk. I suppose that any custom-built functions won't work on the main branch now.

@vinx13
Copy link
Member

vinx13 commented Sep 2, 2021

We should send the callable build_func here as an argument instead of the name of build_func. There will be a new requirement for custom build functions: it requires the custom build_func to be defined in a module importable from PYTHONPATH

@echuraev
Copy link
Contributor

echuraev commented Sep 2, 2021

We should send the callable build_func here as an argument instead of the name of build_func. There will be a new requirement for custom build functions: it requires the custom build_func to be defined in a module importable from PYTHONPATH

Are you sure that cloud pickle will be able to serialize such callable object? It was my first idea, but I faced the problem that I wasn't able to serialize python callable object and then call it from C++ code. I got an error like something like that: "Not able to serialize callable object". Don't remember the whole test, unfortunately. But maybe I missed something and did something wrong.

@vinx13
Copy link
Member

vinx13 commented Sep 2, 2021

Python function is pickled by it's qualified name (that means the module of the function should be importable). It might be different in our case I'll double check

@tqchen
Copy link
Member

tqchen commented Sep 2, 2021

we can serialize callable functions(through cloud pickle), but the requirement is that the callable functions can only call functions in the tvm namespace and should be available in https://github.com/apache/tvm/blob/main/python/tvm/exec/popen_worker.py

It does mean that if we call functions that are not by default imported, we might need to do

def custom_func():
    # import here so things are available in the callee side
    import dep1
    dep1.xyz

@tqchen
Copy link
Member

tqchen commented Sep 2, 2021

Additionally, because some of the custom function might need to use functions from tvm.contrib.ndk, and others are available(or we need to import them in the function body). The easiest way is to import them in popen_worker by default.

@echuraev
Copy link
Contributor

echuraev commented Sep 3, 2021

@tqchen, @vinx13 I tried to do that (passing callable function as an argument) and specially I commited these changes on my local branch: https://github.com/echuraev/tvm/tree/echuraev/fix_measure_build_func

But it doesn't work. I got the following error:

Get 64 programs to measure:
Traceback (most recent call last):
  File "exp_nn/tune_my_model.py", line 148, in <module>
    runner()
  File "exp_nn/tune_my_model.py", line 143, in runner
    run_tuning(tasks, task_weights, log_file)
  File "exp_nn/tune_my_model.py", line 93, in run_tuning
    tuner.tune(tune_option)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/auto_scheduler/task_scheduler.py", line 357, in tune
    self._tune_task(idx)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/auto_scheduler/task_scheduler.py", line 452, in _tune_task
    measure_inputs, measure_results = self.search_policies[task_idx].continue_search_one_round(
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/auto_scheduler/search_policy.py", line 119, in continue_search_one_round
    return _ffi_api.SearchPolicyContinueSearchOneRound(self, num_measure, measurer)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/_ffi/_ctypes/packed_func.py", line 237, in __call__
    raise get_last_ffi_error()
ValueError: Traceback (most recent call last):
  [bt] (8) 9   libtvm.dylib                        0x000000011d4eb681 tvm::runtime::TVMRetValue tvm::runtime::PackedFunc::operator()<tvm::runtime::Array<tvm::auto_scheduler::MeasureInput, void> const&, int&, int&, tvm::runtime::PackedFunc&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, int&>(tvm::runtime::Array<tvm::auto_scheduler::
MeasureInput, void> const&, int&, int&, tvm::runtime::PackedFunc&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, int&) const + 401
  [bt] (7) 8   libtvm.dylib                        0x000000011d447b71 std::__1::function<void (tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*)>::operator()(tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*) const + 65
  [bt] (6) 7   libtvm.dylib                        0x000000011d447d9f std::__1::__function::__value_func<void (tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*)>::operator()(tvm::runtime::TVMArgs&&, tvm::runtime::TVMRetValue*&&) const + 95
  [bt] (5) 6   libtvm.dylib                        0x000000011f707d86 std::__1::__function::__func<TVMFuncCreateFromCFunc::$_2, std::__1::allocator<TVMFuncCreateFromCFunc::$_2>, void (tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*)>::operator()(tvm::runtime::TVMArgs&&, tvm::runtime::TVMRetValue*&&) + 70
  [bt] (4) 5   libtvm.dylib                        0x000000011f7092c7 std::__1::__function::__alloc_func<TVMFuncCreateFromCFunc::$_2, std::__1::allocator<TVMFuncCreateFromCFunc::$_2>, void (tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*)>::operator()(tvm::runtime::TVMArgs&&, tvm::runtime::TVMRetValue*&&) + 71
  [bt] (3) 4   libtvm.dylib                        0x000000011f709317 void std::__1::__invoke_void_return_wrapper<void, true>::__call<TVMFuncCreateFromCFunc::$_2&, tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*>(TVMFuncCreateFromCFunc::$_2&, tvm::runtime::TVMArgs&&, tvm::runtime::TVMRetValue*&&) + 71
  [bt] (2) 3   libtvm.dylib                        0x000000011f7093b3 decltype(std::__1::forward<TVMFuncCreateFromCFunc::$_2&>(fp)(std::__1::forward<tvm::runtime::TVMArgs>(fp0), std::__1::forward<tvm::runtime::TVMRetValue*>(fp0))) std::__1::__invoke<TVMFuncCreateFromCFunc::$_2&, tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*>(TVMFuncCreateFromCFunc::$_2&, tvm::runtime::T
VMArgs&&, tvm::runtime::TVMRetValue*&&) + 115
  [bt] (1) 2   libtvm.dylib                        0x000000011f70947f TVMFuncCreateFromCFunc::$_2::operator()(tvm::runtime::TVMArgs, tvm::runtime::TVMRetValue*) const + 175
  [bt] (0) 1   libtvm.dylib                        0x000000011f74cb55 tvm::runtime::Backtrace() + 37
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/_ffi/_ctypes/packed_func.py", line 81, in cfun
    rv = local_pyfunc(*pyargs)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/auto_scheduler/measure.py", line 697, in local_builder_build
    for res in tuple_res:
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/concurrent/futures/_base.py", line 611, in result_iterator
    yield fs.pop().result()
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/concurrent/futures/_base.py", line 439, in result
    return self.__get_result()
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
    raise self._exception
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/contrib/popen_pool.py", line 390, in <lambda>
    worker = lambda x: self._worker_run_with_error_catching(fn, (x,), None)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/contrib/popen_pool.py", line 343, in _worker_run_with_error_catching
    return MapResult(status=StatusKind.COMPLETE, value=self._worker_run(fn, args, kwargs))
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/contrib/popen_pool.py", line 338, in _worker_run
    proc.send(fn, args, kwargs, self._timeout)
  File "/Users/echuraev/Workspace/OctoML/tvm/python/tvm/contrib/popen_pool.py", line 224, in send
    data = cloudpickle.dumps((fn, args, kwargs, timeout), protocol=pickle.HIGHEST_PROTOCOL)
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/site-packages/cloudpickle/cloudpickle_fast.py", line 73, in dumps
    cp.dump(obj)
  File "/Users/echuraev/opt/anaconda3/lib/python3.8/site-packages/cloudpickle/cloudpickle_fast.py", line 563, in dump
    return Pickler.dump(self, obj)
ValueError: ctypes objects containing pointers cannot be pickled

One interesting thing that I tried to dump each of these parameters in tuple (fn, args, kwargs, timeout) separately and I didn't see this error. I suppose that the problem is happened because fn is a PackedFunction with python function. And CloudPickle is not able to serialize native PackedFunction object.

Another idea how we can quick fix this problem: move tar and ndk functions to the _local_build_worker and pass only names "tar" or "ndk" to it and select implementation there. In this case it would work for current tvm defined custom functions. In case when passed name is not "tar" or "ndk", then we will have another argument with callable function. It won't work now. Because of this problem with packed functions but we can fix it in the next PR.

@vinx13
Copy link
Member

vinx13 commented Sep 3, 2021

The finding looks correct. Only python function can be sent. PackedFunc cannot be pickled. But they can be looked up by name they are also registered in the worker

@vinx13
Copy link
Member

vinx13 commented Sep 3, 2021

@Wanger-SJTU @echuraev can you check if this fix works? vinx13@23c5ffc

@echuraev
Copy link
Contributor

echuraev commented Sep 6, 2021

@Wanger-SJTU @echuraev can you check if this fix works? vinx13@23c5ffc

I checked, and yes, it works! Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants