Initial version of ConcurrentDict container via TBB hashmap#972
Conversation
Motivation: SDC relies on typed.Dict implementation in many core pandas algorithms, and it doesn't support concurrent read/writes. To fill this gap we add ConcurrentDict type which will be used if threading layer is TBB.
|
Hello @kozlov-alexey! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found: There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻 Comment last updated at 2021-05-14 14:58:21 UTC |
|
/AzurePipelines Run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
|
||
| { | ||
| auto ksize = this->key_info.size; | ||
| void* _key = malloc(ksize); |
There was a problem hiding this comment.
Why not new? Can we wrap these explicit mallocs/frees into unique_ptr?
There was a problem hiding this comment.
It's problematic. I've tried a bit, but using unique_ptr<char[ksize]> is not possible since ksize is not compile-time const. Also the fact that TBB hashmap has to store the raw pointer (since HashCompare is actually numba functions that expect key/value to be pointers to Numba structs) kind of decrease the value of it (i.e. the ownership of allocated memory should go to hashmap itself and this function frees this memory only if insertion failed).
There was a problem hiding this comment.
You can use unique_ptr with dynamically-sized arrays. Like this :
auto _key = std::unique_ptr<char[]>(new char[ksize])Or like this:
auto _key = std::make_unique<char[]>(ksize); | found = this->p_map->find(result, key); | ||
| if (found) | ||
| { | ||
| memcpy(res, result->second, this->val_info.size); |
There was a problem hiding this comment.
Clokwork will aggro on memcpy
There was a problem hiding this comment.
I double checked and it appears it doesn't (see report below - all three issues are known and should be fixed by #967). Not sure if memcpy_s will enhance something, but I can still change if you think it's better.
Klocwork 20.1.0.97
-----
May 4, 2021 2:01:17 PM CDT: Running build preparation stage...
May 4, 2021 2:01:17 PM CDT: Build preparation stage completed
May 4, 2021 2:01:18 PM CDT: Running compilation stage...
1> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/_distributed.cpp
2> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/_hiframes.cpp
9> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/native/sort.cpp
8> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/transport/hpat_transport_single_process.cpp
7> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/io/_io.cpp
6> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/_datetime_ext.cpp
4> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/_str_ext.cpp
3> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/_set_ext.cpp
Warning: /localdisk/work/akozlov/miniconda3/kwrun_6/.kwlp/buildspec.txt:24: 'compile' line for object file /localdisk/work/akozlov/miniconda3/hpat/build/temp.linux-x86_64-3.8/sdc/native/utils.o is already defined (ignored)
15> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/native/utils.cpp
14> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/native/module.cpp
11> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/native/stable_sort.cpp
5> CLASSIC: Compiling /localdisk/work/akozlov/miniconda3/hpat/sdc/native/conc_dict_module.cpp
May 4, 2021 2:01:29 PM CDT: Compilation stage completed
May 4, 2021 2:01:29 PM CDT: Running C/C++ defects detection stage...
1> Analyzing _distributed.cpp_0.o
2> Analyzing _hiframes.cpp_1.o
3> Analyzing _set_ext.cpp_2.o
4> Analyzing _str_ext.cpp_3.o
5> Analyzing _datetime_ext.cpp_4.o
6> Analyzing _io.cpp_5.o
7> Analyzing hpat_transport_single_process.cpp_6.o
8> Analyzing utils.cpp_10.o
9> Analyzing sort.cpp_7.o
10> Analyzing stable_sort.cpp_8.o
11> Analyzing module.cpp_9.o
12> Analyzing conc_dict_module.cpp_12.o
May 4, 2021 2:02:21 PM CDT: C/C++ defects detection stage completed
May 4, 2021 2:02:21 PM CDT: Running linking stage...
May 4, 2021 2:02:22 PM CDT: Linking stage completed
1 (Local) /localdisk/work/akozlov/miniconda3/hpat/sdc/native/sort.cpp:151 VOIDRET (2:Error) Analyze
void function returns value
2 (Local) /localdisk/work/akozlov/miniconda3/hpat/sdc/native/utils.cpp:89 NPD.CHECK.MUST (1:Critical) Analyze
Pointer 'this->arena._M_t' checked for NULL at line 86 will be dereferenced at line 89.
3 (Local) /localdisk/work/akozlov/miniconda3/hpat/sdc/native/stable_sort.cpp:340 VOIDRET (2:Error) Analyze
void function returns value
Summary: 3 Local
3 Total Issue(s)
| if skip_check and skip_check(suffix): | ||
| continue | ||
| full_func_name = f'{fname}_{suffix}' | ||
| ll.add_symbol(full_func_name, |
There was a problem hiding this comment.
Is there a reason, we are using ll.add_symbol and not ctypes binding, so we can directly call functions from jit code?
Like it is done here:
https://github.com/IntelPython/sdc/blob/master/sdc/functions/sort.py#L55
There was a problem hiding this comment.
No, there's no particular reason for this (except it was easier to write LLVM codegen that way - the function call inserted there directly refers to the function name being loaded, so there's 1:1 mapping and it's easier to navigate the code). I think we may refactor this and re-use common ctypes/bind approach later.
|
|
||
|
|
||
| @intrinsic | ||
| def hashmap_clear(typingctx, dict_type): |
There was a problem hiding this comment.
why do we need this to be as intrinsic and not as an overload?
@overload(hashmap_clear)
def hashmap_clear_ovld(conc_dict):
ty_key, ty_val = dict_type.key_type, dict_type.value_type
key_type_postfix, value_type_postfix = _get_types_postfixes(ty_key, ty_val)
hasmap_clear_func = hashmap_clear_map[(key_type_postfix, value_type_postfix)]
def hashmap_clear_impl(conc_dict):
return hashmap_clear_func(conc_dict._data_ptr)
return hashmap_clear_implAm I missing something?
ctypes binding is required for this
There was a problem hiding this comment.
Thanks! I didn't consider this way of doing things. However it may be more difficult to read for methods like set and pop which make key transformations during codegen or allocate the result (e.g.
sdc/sdc/extensions/sdc_hashmap_ext.py
Line 434 in 1177495
| { | ||
| typename map_type::const_accessor result; | ||
| found = this->p_map->find(result, key); | ||
| result.release(); |
There was a problem hiding this comment.
Do we really need both - inner scope and call to release?
There was a problem hiding this comment.
No of course, but it's (I guess) usual practice to release immediately after you don't need the lock (i.e. I copy-pasted it from TBB examples :)).
|
|
||
|
|
||
| template<typename key_type, typename val_type> | ||
| void hashmap_dump(void* p_hash_map) |
There was a problem hiding this comment.
Can we make hashmap_dump and corresponding #include <iostream> optional and wrap in some ifdefs? I would really prefer release libraries to be without iostream dependency?
Motivation: SDC relies on typed.Dict implementation in many core
pandas algorithms, and it doesn't support concurrent read/writes.
To fill this gap we add ConcurrentDict type which will be used if
threading layer is TBB.