object_threadsafe icon indicating copy to clipboard operation
object_threadsafe copied to clipboard

Make get_or_set_index immune to memory address reuse

Open CatboxParadox opened this issue 4 years ago • 0 comments

If a mutex instance gets used, deleted and then recreated in the same thread - especially on the stack, the new instance might get the same memory address as the old instance. Because of this, a lookup in thread_local_index_hashmap can't be based on the instance address (this), but rather on the (heap allocated, shared) shared_locks_array instance address, because that one will not be reused even after the mutex instance is deleted since all unregister_t structs still hold a std::shared_ptr to it until they get erased from thread_local_index_hashmap.

I only ran the benchmark from object_threadsafe/benchmark, hope that's fine, I would expect the fix to have no impact on performance:

Before the change:

CPU Cores: 8
Benchmark thread-safe associative containers with size = 100000
Threads = 8, iterations per thread = 2000000
Time & MOps - steady_clock is_steady = 1, num/den = 1 / 1000000000
Latency     - high_resolution_clock is_steady = 0, num/den = 1 / 1000000000
burn_cpu() for each multithread operations took: 3.26855 nano-sec 

Filling of containers... filled containers.
0        % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    9.02    1.77
std::map & std::mutex:  20.8    0.77
safe_ptr<map,mutex>:    20.4    0.785
safe_ptr<map,shared>:   2.29    6.99
safe_ptr<map,contfree>: 1.3     12.3
safe<map,contf>rowlock: 1.35    11.8
safe part<mutex>:       3.83    4.18
safe part<contfree>:    1.51    10.6
15       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    5.35    2.99
std::map & std::mutex:  14.4    1.11
safe_ptr<map,mutex>:    17      0.944
safe_ptr<map,shared>:   29.5    0.543
safe_ptr<map,contfree>: 3.42    4.68
safe<map,contf>rowlock: 2.68    5.96
safe part<mutex>:       3.22    4.97
safe part<contfree>:    1.59    10
30       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.48    3.58
std::map & std::mutex:  15.4    1.04
safe_ptr<map,mutex>:    14.2    1.13
safe_ptr<map,shared>:   35.6    0.449
safe_ptr<map,contfree>: 4.83    3.32
safe<map,contf>rowlock: 3.78    4.24
safe part<mutex>:       3.7     4.32
safe part<contfree>:    1.74    9.21
45       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.57    3.5
std::map & std::mutex:  14.7    1.09
safe_ptr<map,mutex>:    13.6    1.18
safe_ptr<map,shared>:   35.6    0.45
safe_ptr<map,contfree>: 6.27    2.55
safe<map,contf>rowlock: 5.03    3.18
safe part<mutex>:       3.83    4.18
safe part<contfree>:    1.98    8.08
60       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    5.09    3.14
std::map & std::mutex:  15.1    1.06
safe_ptr<map,mutex>:    15      1.07
safe_ptr<map,shared>:   33.9    0.472
safe_ptr<map,contfree>: 7.97    2.01
safe<map,contf>rowlock: 6.53    2.45
safe part<mutex>:       3.97    4.03
safe part<contfree>:    2.17    7.36
75       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.66    3.43
std::map & std::mutex:  14.9    1.07
safe_ptr<map,mutex>:    14.8    1.08
safe_ptr<map,shared>:   30      0.533
safe_ptr<map,contfree>: 8.79    1.82
safe<map,contf>rowlock: 7.6     2.1
safe part<mutex>:       4.11    3.89
safe part<contfree>:    2.36    6.79
90       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.77    3.36
std::map & std::mutex:  15.2    1.05
safe_ptr<map,mutex>:    15.4    1.04
safe_ptr<map,shared>:   23.5    0.68
safe_ptr<map,contfree>: 9.7     1.65
safe<map,contf>rowlock: 8.96    1.79
safe part<mutex>:       4.33    3.7
safe part<contfree>:    2.68    5.97
end

After the change:

CPU Cores: 8
Benchmark thread-safe associative containers with size = 100000
Threads = 8, iterations per thread = 2000000
Time & MOps - steady_clock is_steady = 1, num/den = 1 / 1000000000
Latency     - high_resolution_clock is_steady = 0, num/den = 1 / 1000000000
burn_cpu() for each multithread operations took: 3.89929 nano-sec
Filling of containers... filled containers.
0        % of write operations (1/3 insert, 1/3 delete, 1/3 update)
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max
std::map - 1 thread:    9.13    1.75
std::map & std::mutex:  21.6    0.739
safe_ptr<map,mutex>:    21      0.76
safe_ptr<map,shared>:   2.25    7.12
safe_ptr<map,contfree>: 1.51    10.6
safe<map,contf>rowlock: 1.47    10.9
safe part<mutex>:       4.11    3.9
safe part<contfree>:    1.72    9.28
15       % of write operations (1/3 insert, 1/3 delete, 1/3 update)
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max
std::map - 1 thread:    5.28    3.03
std::map & std::mutex:  15.4    1.04
safe_ptr<map,mutex>:    18.5    0.867
safe_ptr<map,shared>:   28.8    0.556
safe_ptr<map,contfree>: 3.95    4.05
safe<map,contf>rowlock: 2.74    5.85
safe part<mutex>:       3.41    4.7
safe part<contfree>:    1.76    9.07
30       % of write operations (1/3 insert, 1/3 delete, 1/3 update)
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max
std::map - 1 thread:    4.45    3.59
std::map & std::mutex:  15.5    1.03
safe_ptr<map,mutex>:    15.3    1.05
safe_ptr<map,shared>:   34.7    0.461
safe_ptr<map,contfree>: 5.13    3.12
safe<map,contf>rowlock: 4.07    3.94
safe part<mutex>:       3.85    4.16
safe part<contfree>:    1.83    8.76
45       % of write operations (1/3 insert, 1/3 delete, 1/3 update)
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max
std::map - 1 thread:    4.67    3.43
std::map & std::mutex:  15.4    1.04
safe_ptr<map,mutex>:    15      1.07
safe_ptr<map,shared>:   35.3    0.454
safe_ptr<map,contfree>: 6.41    2.5
safe<map,contf>rowlock: 5.25    3.05
safe part<mutex>:       4.01    3.99
safe part<contfree>:    2.03    7.88
60       % of write operations (1/3 insert, 1/3 delete, 1/3 update)
                                        (1 Operation latency, usec)
std::map - 1 thread:    4.83    3.32
std::map & std::mutex:  15.6    1.03
safe_ptr<map,mutex>:    15.3    1.04
safe_ptr<map,shared>:   32.7    0.489
safe_ptr<map,contfree>: 7.74    2.07
safe<map,contf>rowlock: 6.36    2.52
safe part<mutex>:       4.04    3.96
safe part<contfree>:    2.69    5.94
75       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.89    3.27
std::map & std::mutex:  15.3    1.05
safe_ptr<map,mutex>:    15.4    1.04
safe_ptr<map,shared>:   29.8    0.538
safe_ptr<map,contfree>: 8.79    1.82
safe<map,contf>rowlock: 7.65    2.09
safe part<mutex>:       4.26    3.75
safe part<contfree>:    2.45    6.54
90       % of write operations (1/3 insert, 1/3 delete, 1/3 update) 
                                        (1 Operation latency, usec)
                     time, sec   MOps    Median  Min     Max 
std::map - 1 thread:    4.73    3.38
std::map & std::mutex:  15.7    1.02
safe_ptr<map,mutex>:    15.9    1.01
safe_ptr<map,shared>:   23.7    0.674
safe_ptr<map,contfree>: 9.5     1.68
safe<map,contf>rowlock: 8.77    1.83
safe part<mutex>:       4.48    3.58
safe part<contfree>:    2.64    6.07
end

CatboxParadox avatar Jul 27 '21 10:07 CatboxParadox