-
Notifications
You must be signed in to change notification settings - Fork 369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iox-#2177 Update SPSC SoFi #2214
base: main
Are you sure you want to change the base?
Conversation
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
From #2178 (comment)
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #2214 +/- ##
==========================================
- Coverage 79.09% 78.06% -1.03%
==========================================
Files 431 432 +1
Lines 16581 15904 -677
Branches 2300 2301 +1
==========================================
- Hits 13114 12415 -699
- Misses 2616 2644 +28
+ Partials 851 845 -6
Flags with carried forward coverage won't be shown. Click here to find out more.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for letting you wait so long for the review. I needed a fresh mind to review lock-free code :)
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
- add comments to clarify concurrent behavior - fix memory orders
1892aa0
to
8cc9277
Compare
@albtam just to be sure that you are not waiting on feedback. I'll wait until the CI (at least the github actions) are green. |
@elBoberido Back to you |
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.hpp
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
// While memory synchronization is not needed with m_readPosition, we need | ||
// memory_order_acquire to avoid the reordering of the operation | ||
uint64_t currentReadPosition = m_readPosition.load(std::memory_order_acquire); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This corresponds to the m_readPosition.compare_exchange_weak
store operation in pop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, what I wanted to say is that the memory synchronization is not required. But to avoid the reordering, we need a corresponding store/release. So in the end, there will be a memory synchronization but this is only a side effect. Do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree. What do you think about talking about happens before
relationships instead of memory synchronization
and reordering
? This is a more generic term and from the context it becomes clear if the happens before relationship is required for memory or for logic.
In this specific case the comment could become something like We need to establish a happens before relationship with 'm_writePosition.load(...)'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@elBoberido Sorry, can you remind me what's the happens-before relationship we want to enforce here? Since it's a load/acquire, it doesn't influence the order of operations above (so it can't be m_writePosition.load(...)
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. This specific one acts as a sync point to a store
in pop
and I think we can remove it since the CAS below now has the correct ordering and does an acquire
load on success. We might need to change the failure ordering from relaxed
to acquire
to still act as a sync point to the store
in pop
but I'm not fully sure about this. Maybe @elfenpiff, @FerdinandSpitzschnueffler, @MatthiasKillat @budrus could also share their expertise.
We can do the same with the CAS in the pop
method but then the early return needs to be removed and replaced with the previous logic again. Overall, I think it would be more performant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might need to change the failure ordering from relaxed to acquire to still act as a sync point to the store in pop but I'm not fully sure about this.
I think we do need to make it acquire
We can do the same with the CAS in the pop method but then the early return needs to be removed and replaced with the previous logic again. Overall, I think it would be more performant.
Not sure to understand what you want to do here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean we can make uint64_t currentReadPosition = m_readPosition.load(std::memory_order_acquire);
in pop
also relaxed
but then the early return needs to be removed again and the previous logic with popWasSuccessful
needs to be re-introduced.
Overall, this would lead to only one acquire
and one release
for m_writePosition
and two acq_rel
/acquire
for m_readPosition
which simplifies the relation of the sync points.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, changed
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
@elBoberido Thanks. I addressed most of your comments. There are still some points to clarify |
@albtam you can undo some of my suggestions 😅 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This somehow becomes a never ending story but with the revert of the relaxed
ordering in the places where acq_rel
was required, we might be able to relax some of the acquire
orderings. Unfortunately this is something which is hard to see when there are too many wrong orderings but once the ordering is at least as strong as required, we can identify the locations which definitely need the strong ordering and can try to relax some orderings which act as sync points when we identify new sync points or change the algorithm a bit.
iceoryx_hoofs/concurrent/buffer/include/iox/detail/spsc_sofi.inl
Outdated
Show resolved
Hide resolved
I pushed some of your proposals. There is still something unclear. Lock-free is hard :P |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, lock-free is hard 😅
Btw, can you please adjust your commit messages with the issue number.
Before this is merged, it would be good to run test_stress_spsc_sofi
. Ideally each of the three test for a few hours. The stress time can only be adjusted in the source code.
@@ -206,14 +206,14 @@ inline bool SpscSofi<ValueType, CapacityValue>::push(const ValueType& valueIn, V | |||
// Another issue might be that two consecutive pushes (not concurrent) happen on different CPU | |||
// cores without synchronization, then the memory also needs to be synchronized for the overflow | |||
// case. | |||
// Memory order failure is memory_order_relaxed since there is no further synchronization //// | |||
// Memory order failure is memory_order_relaxed since there is no further synchronization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the order is changed to memory_order_acquire
, the comment is not true anymore 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed
// While memory synchronization is not needed with m_readPosition, we need | ||
// memory_order_acquire to avoid the reordering of the operation | ||
uint64_t currentReadPosition = m_readPosition.load(std::memory_order_acquire); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean we can make uint64_t currentReadPosition = m_readPosition.load(std::memory_order_acquire);
in pop
also relaxed
but then the early return needs to be removed again and the previous logic with popWasSuccessful
needs to be re-introduced.
Overall, this would lead to only one acquire
and one release
for m_writePosition
and two acq_rel
/acquire
for m_readPosition
which simplifies the relation of the sync points.
eedfddc
to
04837bb
Compare
@elBoberido I implemented your last recommendations. |
@albtam sorry, this totally slipped my attention. The changes look good. Did you have a chance to run the stress tests? |
Pre-Review Checklist for the PR Author
iox-123-this-is-a-branch
)iox-#123 commit text
)task-list-completed
)iceoryx_hoofs
are added to./clang-tidy-diff-scans.txt
Notes for Reviewer
Checklist for the PR Reviewer
iceoryx_hoofs
have been added to./clang-tidy-diff-scans.txt
Post-review Checklist for the PR Author
References