Implement redisAtomic to replace _Atomic C11 builtin#7707
Implement redisAtomic to replace _Atomic C11 builtin#7707oranagra merged 9 commits intoredis:unstablefrom
Conversation
|
If users want to use c11 _Atomic, they could modify Makefile |
|
Would you mind having a look if you have time @JohnSully I notice that you use many atomic variables in your KeyDB. |
|
Thanks @ShooterIT !
i would rather you find the commit that removed them and logically revert it, since there's a risk that these changed after they were first introduced (so we're reinstating their old version). Maybe we want to suppress these helgrind warnings and add it to run in the daily CI github action? Would C11 _Atomic provide any efficiency improvement here? if so, maybe we want to change the Makefile to automatically use it when supported? Can you please test that it builds on Centos7 and possibly 6 too. @yossigo please take a look. |
this is the commit dd5b105, I think i did revert logically. Please help me review it. I test with Helgrind just because i want to find errors early. I don't find C11 _Atomic has efficiency improvement, i provide it because some people would like to use new features of language and more importantly we ever supported it. I have no idea to change the Makefile to automatically use _Atomic when supported, maybe target is a good idea. I'm sorry i don't have Centos, my company machine kernel is as follow, and two gcc versions of 4.8(not support _Atomic) and 8.2((supports _Atomic)) are installed on it. All redis tests are passed both of two versions. |
|
Hi @ShooterIT I'm not sure I understand the need for introducing these in all the places you've chosen, e.g. in createClient. Is this based upon the output of a tool? Note that your changes are not free, and will introduce performance regressions especially on ARM. That said for the atomics you've changed from relaxed to higher consistency will generally only repro on ARM if they were bugs anyways. KeyDB of course uses atomics a lot but our threading model is also very different so its not a guide for what should be Atomic in Redis. |
|
Thanks @JohnSully
Salvatore did add comment on his commits. Yes, for these variable, I also think it is safe without being defined as atomic variable.
The default behavior of C11 _Atomic operations provides for sequentially consistent ordering, so now it should not have performance regressions than before.
Even so, I still use ’sequentially consistent‘ semantic just the same as before, it need not require much complicated derivation and analysis for correctness. Redis can run on many different archs, we should guarantee correctness theoretically. And more and more people take part in developing redis, that also is very import to make it easy to understand and make modification less error-prone. What's more, actually I don't find there are no obvious performance difference on x86 arch between 'acquire/release' and 'sequentially consistent'. For ARM, there are some changes, the CPU instructions of different atomic variable operations become the same gradually https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html. Maybe CPU and compiler designers want to make it easy to use because it is too hard actually. I borrow one ARM server(as follow) from my colleague, both 'relaxed' and 'consistent' memory order of atomic variables can run correctly and without much performance difference. I know KeyDB has different threading model, but i think you have much experience with atomic variable. Thank you very much for reviewing my PR. |
b25745f to
c88d275
Compare
|
@oranagra For using C11 _Atomic, how about this |
|
@ShooterIT if there are no downsides of using the other atomic types (gcc and sync builtins) then i prefer to stick to c99 even if c11 is supported. the benefit is that if there are any other differences we always build and test with c99 (and not just rarely do so), and it'll present any use of a c11 feature to creep in and be detected only in a If there are any performance advantages with _Atomic, then your suggested patch above makes sense. |
|
Another thought on that subject, all the maybe we don't need to support the mutex fallback at all? or maybe we should add a |
|
hi @oranagra AFAIK, operations of c11 _Atomic is the same as atomic builtins for gcc implementation. this is part of the code in <stdatomic.h> of gcc 8.2 on my linux server. So i think there is no much difference between c11 _Atomic and gcc atomic builtins. For |
|
@ShooterIT i'm not certain if we want to remove the code for that fallback or not. after all, the whole point of this PR is to make redis more portable than it is in 6.0. (maybe some old or embedded system may still depend on them?) So i'm not certain, i wanna trim this code. @yossigo what do you think? What I am certain, is that i don't like dead code be completely uncovered by the test (like if we remove |
|
@oranagra Copy that, i wait for your final conclusion. To test dead code |
|
Let's see what we're going to do, @oranagra
|
|
Following a discussion with @yossigo we feel that we should keep supporting _Atomic (since this is the only one that's standard, maybe some day a compiler will support that and not the others), and also because it doesn't add a lot of mess (local to atomicvar.h). Regarding the pthread_mutex, we concluded that we should drop them. @redis/core-team any objections? |
|
Yes, C11 _Atomic is standard, It offers exact definition of atomic variable operations semantic, and i think we will _Atomic gradually. This is standard! I also agree that we drop |
…7600) A first step to enable a consistent full percentile analysis on query latency so that we can fully understand the performance and stability characteristics of the redis-server system we are measuring. It also improves the instantaneous reported metrics, and the csv output format.
|
@oranagra I'm okay with that proposal. |
19f7cef to
6e94789
Compare
|
@oranagra I remove How to support C11 _Atomic, how about #7707 (comment) |
|
@ShooterIT the commit that removes the mutexes looks fine. my problem with the makefile change you suggested in #7707 (comment) is that it'll normally use _Atomic and we're unlikely to know notice that the alternative is broken, or if we violated redis by using some C11 feature elsewhere. i suppose we'll detect that in the daily CI when testing CentOS7 (after we remove the SCL portions). please add a commit that removes the redis/.github/workflows/daily.yml Line 126 in 86511bb come to think of it, the above argument goes the other way around too, i.e. if we always use atomic_builtins, how do we know that the _Atomic is not broken? so maybe indeed the best thing would be to use C11 by default and then at least we have the CentOS7 CI job cover one of the alternatives. what do you think? |
|
For C11 _Atomic, my operation system is macOS and complier is clang, it will report errors if i didn't declare the variables with It will be more safe if we use C11 _Atomic by default, it also is safe if we forget to access them with our atomic macros. Our initial motivation is to make redis more compatible and keep it run correctly. I agree with using C11 by default, but we need cover both atomic builtin and sync builtin, make sure it can be compiled at least. |
For some old version compliers, they will report errors or warnings, if we redefine function type.
CI jobs build redis on CentOS6 and CentOS7 and Daily jobs run the tests on these. We install gcc on these by default in order to cover different compiler versions, gcc is 4.4.7 by default installation on CentOS6 and 4.8.5 on CentOS7.
We include 'stdatomic.h' in our test because gcc 4.8 misses 'stdatomic.h', please see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58016.
127b165 to
37546c4
Compare
|
@oranagra Let's me summarize all works for this PR. Redis 6.0 introduces I/O threads, it is so cool and efficient, we use C11 _Atomic to establish inter-thread synchronization without mutex. But the compiler that must supports C11 _Atomic can compile redis code, that brings a lot of inconvenience since some common platforms can't support by default such as CentOS7, so we want to implement redis atomic type to make it more portable. We have implemented our atomic variable for redis that only has 'relaxed' operations in src/atomicvar.h, so we implement some operations with 'sequentially-consistent', just like the default behavior of C11 _Atomic that can establish inter-thread synchronization. And we replace all uses of C11 _Atomic with redis atomic variable. Our implementation of redis atomic variable uses C11 _Atomic, __atomic or __sync macros if available, it supports most common platforms, and we will detect automatically which feature we use. In Makefile we use a dummy file to detect if the compiler supports C11 _Atomic. Now for gcc, we can compile redis code theoretically if your gcc version is not less than 4.1.2(starts to support __sync_xxx operations). Otherwise, we remove use mutex fallback to implement redis atomic variable for performance and test. You will get compiling errors if your compiler doesn't support all features of above. For cover redis atomic variable tests, we add other CI jobs that build redis on CentOS6 and CentOS7 and workflow daily jobs that run the tests on them. For them, we just install gcc by default in order to cover different compiler versions, gcc is 4.4.7 by default installation on CentOS6 and 4.8.5 on CentOS7. We restore the feature that we can test redis with Helgrind to find data race errors. But you need install Valgrind in the default path configuration firstly before running your tests, since we use macros in helgrind.h to tell Helgrind inter-thread happens-before relationship explicitly for avoiding false positives. Please open an issue on github if you find data race errors relate to this commit. Unrelated: |
|
@redis/core-team this PR is ready for review. please have a look at the previous post for a summary (and squash commit comment). |
|
@ShooterIT thanks a lot for handling this. |
Redis 6.0 introduces I/O threads, it is so cool and efficient, we use C11 _Atomic to establish inter-thread synchronization without mutex. But the compiler that must supports C11 _Atomic can compile redis code, that brings a lot of inconvenience since some common platforms can't support by default such as CentOS7, so we want to implement redis atomic type to make it more portable. We have implemented our atomic variable for redis that only has 'relaxed' operations in src/atomicvar.h, so we implement some operations with 'sequentially-consistent', just like the default behavior of C11 _Atomic that can establish inter-thread synchronization. And we replace all uses of C11 _Atomic with redis atomic variable. Our implementation of redis atomic variable uses C11 _Atomic, __atomic or __sync macros if available, it supports most common platforms, and we will detect automatically which feature we use. In Makefile we use a dummy file to detect if the compiler supports C11 _Atomic. Now for gcc, we can compile redis code theoretically if your gcc version is not less than 4.1.2(starts to support __sync_xxx operations). Otherwise, we remove use mutex fallback to implement redis atomic variable for performance and test. You will get compiling errors if your compiler doesn't support all features of above. For cover redis atomic variable tests, we add other CI jobs that build redis on CentOS6 and CentOS7 and workflow daily jobs that run the tests on them. For them, we just install gcc by default in order to cover different compiler versions, gcc is 4.4.7 by default installation on CentOS6 and 4.8.5 on CentOS7. We restore the feature that we can test redis with Helgrind to find data race errors. But you need install Valgrind in the default path configuration firstly before running your tests, since we use macros in helgrind.h to tell Helgrind inter-thread happens-before relationship explicitly for avoiding false positives. Please open an issue on github if you find data race errors relate to this commit. Unrelated: - Fix redefinition of typedef 'RedisModuleUserChangedFunc' For some old version compilers, they will report errors or warnings, if we re-define function type.
|
@ShooterIT i noticed that |
|
hi, @guybe7 i just kept old method before 6.0, https://github.com/redis/redis/blob/5.0/src/server.c#L1077 |
**Bug Fixes:** * [redis#7385](RediSearch/RediSearch#7385) Fix high temporary memory consumption when loading multiple search indexes from RDB * [redis#7430](RediSearch/RediSearch#7430) Fix a potential deadlock in `FT.HYBRID` in cluster mode during updates. * [redis#7454](RediSearch/RediSearch#7454) Fix a garbage collection performence regression * [redis#7460](RediSearch/RediSearch#7460) Fix potential double-free in Fork GC error paths * [redis#7455](RediSearch/RediSearch#7455) Fix internal cursors not being deleted promptly in cluster mode * [redis#7667](RediSearch/RediSearch#7667) Fix a cursor logical leak upon dropping the index * [redis#7796](RediSearch/RediSearch#7796) Fix a potential use-after-free when removing connections * [redis#7792](RediSearch/RediSearch#7792) Fix string comparison for binary data with embedded NULLs in TOLIST reducer in FT.AGGREGATE * [redis#7823](RediSearch/RediSearch#7823) Update `FT.HYBRID` to accept vector blobs only via parameters * [redis#7903](RediSearch/RediSearch#7903) Fix a memory leak in Hybrid ASM * [redis#8052](RediSearch/RediSearch#8052) Fix `FT.HYBRID` behavior when used with `LOAD *` * [redis#8082](RediSearch/RediSearch#8082) Fix incorrect FULLTEXT field metric counts * [redis#8089](RediSearch/RediSearch#8089) Fix an edge case in `CLUSTERSET` handling * [redis#8152](RediSearch/RediSearch#8152) Fix configuration registration issues **Improvements:** * [redis#7427](RediSearch/RediSearch#7427) Enhance `FT.PROFILE` with vector search execution details * [redis#7431](RediSearch/RediSearch#7431) Ensure full `FT.PROFILE` output is returned on timeout with RETURN policy * [redis#7507](RediSearch/RediSearch#7507) Track timeout warnings and errors in INFO * [redis#7576](RediSearch/RediSearch#7576) Track OOM warnings and errors in INFO * [redis#7612](RediSearch/RediSearch#7612) Track `maxprefixexpansions` warnings and errors in INFO * [redis#7960](RediSearch/RediSearch#7960) Persist query warnings across cursor reads * [redis#7551](RediSearch/RediSearch#7551), [redis#7616](RediSearch/RediSearch#7616), [redis#7622](RediSearch/RediSearch#7622), [redis#7625](RediSearch/RediSearch#7625) Add runtime thread and pending-jobs metrics * [redis#7589](RediSearch/RediSearch#7589) Support multiple slot ranges in `search.CLUSTERSET` * [redis#7707](RediSearch/RediSearch#7707) Add `WITHCOUNT` support to `FT.AGGREGATE` * [redis#7862](RediSearch/RediSearch#7862) Add support for subquery `COUNT` in `FT.HYBRID` * [redis#8087](RediSearch/RediSearch#8087) Add warnings when cursor results may be affected by ASM and expose ASM warnings in `FT.PROFILE` * [redis#8049](RediSearch/RediSearch#8049) Add logging for index-related commands * [redis#8150](RediSearch/RediSearch#8150) Fix shard total profile time reporting in `FT.PROFILE`
**Bug Fixes:** * [#7385](RediSearch/RediSearch#7385) Fix high temporary memory consumption when loading multiple search indexes from RDB * [#7430](RediSearch/RediSearch#7430) Fix a potential deadlock in `FT.HYBRID` in cluster mode during updates. * [#7454](RediSearch/RediSearch#7454) Fix a garbage collection performence regression * [#7460](RediSearch/RediSearch#7460) Fix potential double-free in Fork GC error paths * [#7455](RediSearch/RediSearch#7455) Fix internal cursors not being deleted promptly in cluster mode * [#7667](RediSearch/RediSearch#7667) Fix a cursor logical leak upon dropping the index * [#7796](RediSearch/RediSearch#7796) Fix a potential use-after-free when removing connections * [#7792](RediSearch/RediSearch#7792) Fix string comparison for binary data with embedded NULLs in TOLIST reducer in FT.AGGREGATE * [#7704](RediSearch/RediSearch#7704) Use asynchronous jobs for GC in SVS to accelerate execution * [#7823](RediSearch/RediSearch#7823) Update `FT.HYBRID` to accept vector blobs only via parameters * [#7903](RediSearch/RediSearch#7903) Fix a memory leak in Hybrid ASM * [#8052](RediSearch/RediSearch#8052) Fix `FT.HYBRID` behavior when used with `LOAD *` * [#8082](RediSearch/RediSearch#8082) Fix incorrect FULLTEXT field metric counts * [#8089](RediSearch/RediSearch#8089) Fix an edge case in `CLUSTERSET` handling * [#8152](RediSearch/RediSearch#8152) Fix configuration registration issues **Improvements:** * [#7427](RediSearch/RediSearch#7427) Enhance `FT.PROFILE` with vector search execution details * [#7431](RediSearch/RediSearch#7431) Ensure full `FT.PROFILE` output is returned on timeout with RETURN policy * [#7507](RediSearch/RediSearch#7507) Track timeout warnings and errors in INFO * [#7576](RediSearch/RediSearch#7576) Track OOM warnings and errors in INFO * [#7612](RediSearch/RediSearch#7612) Track `maxprefixexpansions` warnings and errors in INFO * [#7960](RediSearch/RediSearch#7960) Persist query warnings across cursor reads * [#7551](RediSearch/RediSearch#7551), [#7616](RediSearch/RediSearch#7616), [#7622](RediSearch/RediSearch#7622), [#7625](RediSearch/RediSearch#7625) Add runtime thread and pending-jobs metrics * [#7589](RediSearch/RediSearch#7589) Support multiple slot ranges in `search.CLUSTERSET` * [#7707](RediSearch/RediSearch#7707) Add `WITHCOUNT` support to `FT.AGGREGATE` * [#7862](RediSearch/RediSearch#7862) Add support for subquery `COUNT` in `FT.HYBRID` * [#8087](RediSearch/RediSearch#8087) Add warnings when cursor results may be affected by ASM and expose ASM warnings in `FT.PROFILE` * [#8049](RediSearch/RediSearch#8049) Add logging for index-related commands * [#8150](RediSearch/RediSearch#8150) Fix shard total profile time reporting in `FT.PROFILE`
Try to solve #7509
Firstly, I try to explain how
_Atomiccan guarantee our program execute safely. The default behavior of_Atomicoperations provides for sequentially consistent ordering. For threaded IO in redis, we use inter-thread synchronization ofio_threads_pending[i], so other IO threads could see the updated values ofio_threads_listinstead of history values whenio_threads_pending[i]is fired.Currently we implement atomic that only supports
memory_order_relaxedfor redis inatomicvar.h, so I implement operations with sequentially-consistent ordering, just like the default behavior of_Atomic.Currently, atomic variables we used in Redis are as follow
For
server.unixtime,server.lruclock,server.next_client_id, Salvatore started using atomic variable from commitsece6587 1f598fc
I just restored these operations back
For
stat_total_writes_processed,stat_total_reads_processed,stat_net_output_bytes,stat_net_input_bytes, we stilluse redis atomic implemented before,that means we use
memory_order_relaxedaccess operations for them, it is ok because there is no happen-before relationship.For
client_max_querybuf_len, I don't find we write and read it simultaneously in redis, there is no data race, so i think it is safe to read in IO threads.Other work i did is that we can test Redis with Helgrind, Now there still are 3 errors in helgrind report when we run redis with a module, as flow. I think there is no risk. For 1, it means redis still holds 4 locks(io_threads_mutex) when redis exits. For 2, 3, that is because we may acquire two mutex
moduleGILandio_threads_mutexbut they are unrelated actully.