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
Fix/assertion error #182
Fix/assertion error #182
Conversation
With `-Wp,-D_GLIBCXX_ASSERTIONS`, tests fail with an assertion error of the form: ``` /usr/include/c++/12/bits/stl_vector.h:1122: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _T p = float; _Alloc = std::allocator<float>; reference = float&; size_type = long unsigned int]: Assertion '__n < this->size()' failed. ``` The assertion failure happens on line 473 in report_reader.cpp: ``` std::copy(&buffer[gid_start], &buffer[gid_end], &data_ptr[offset]); ``` The problem is that `gid_end` can equal the size of `buffer`, and `&buffer[gid_end]` triggers the assertion failure in that case. Diagnosed and fixed suggested by Jerry James in the Fedora package review: https://bugzilla.redhat.com/show_bug.cgi?id=2061077#c6
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.
Seems sensible from my side, wondering if this will work on all compilers though.
I'm not sure what the corresponding flags for clang are, but I see that the CI builds completed fine so perhaps these extra definitions are simply ignored? |
src/report_reader.cpp
Outdated
@@ -470,7 +470,7 @@ DataFrame<T> ReportReader<T>::Population::get(const nonstd::optional<Selection>& | |||
data_ptr[offset] = buffer[gid_start]; | |||
} else { // Elements report | |||
uint64_t gid_end = range.second - min; | |||
std::copy(&buffer[gid_start], &buffer[gid_end], &data_ptr[offset]); | |||
std::copy(buffer.begin() + gid_start, buffer.begin() + gid_end, &data_ptr[offset]); |
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.
Hmmmm... Do you mind to clarify why the change, please? I don't mind, but if we do change this, we should also change the code in line 470 and we must verify that the performance of the copies remains the same (e.g., this operation is executed thousands of times in thousands of timesteps, it matters!).
On a side note, perhaps using something like std::next(buffer.begin(), gid_start)
as well, but this is irrelevant.
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.
Turning on the _GLIBCXX_ASSERTIONS
makes doing the subscript on buffer look like &buffer[buffer.size()]
, which would be an out of bounds error and thus undefined behavior. AFAIK, this is ok in this specific case as long as std::copy
doesn't deref the pointer.
The change means we use the std::copy
overload that takes iterators, and is "safer", because there is no chance there is an OOB deref, as long as gid_end
never is larger than the size of the array.
The ASM will look the same from both of them, I'd expect, at a reasonable optimization level.
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.
Thanks @mgeplf for clarifying. My point was that accessing the array with []
in std::copy
might avoid boundary checks or additional code, but if you feel that it should be the same, then no complains at all.
I'd still prefer the use of std::next(buffer.begin(), gid_start)
and std::next(buffer.begin(), gid_end)
with iterators, although I don't mind leaving the code as it is with the +
operator.
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.
but if you feel that it should be the same, then no complains at all.
Had a quick look on godbolt to confirm (which I never take as conclusive), but it seems that compiler sees through this:
https://godbolt.org/z/8dez5nEM3
I'd still prefer the use of std::next(buffer.begin(), gid_start)
Ok, I'll make those changes, and another one to remove the pointer usage altogether.
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've made the changes; any/all reviews welcome.
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.
All tests pass in my Fedora build with the newest changes 👍
src/report_reader.cpp
Outdated
@@ -470,7 +470,7 @@ DataFrame<T> ReportReader<T>::Population::get(const nonstd::optional<Selection>& | |||
data_ptr[offset] = buffer[gid_start]; | |||
} else { // Elements report | |||
uint64_t gid_end = range.second - min; | |||
std::copy(&buffer[gid_start], &buffer[gid_end], &data_ptr[offset]); | |||
std::copy(buffer.begin() + gid_start, buffer.begin() + gid_end, &data_ptr[offset]); |
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.
Turning on the _GLIBCXX_ASSERTIONS
makes doing the subscript on buffer look like &buffer[buffer.size()]
, which would be an out of bounds error and thus undefined behavior. AFAIK, this is ok in this specific case as long as std::copy
doesn't deref the pointer.
The change means we use the std::copy
overload that takes iterators, and is "safer", because there is no chance there is an OOB deref, as long as gid_end
never is larger than the size of the array.
The ASM will look the same from both of them, I'd expect, at a reasonable optimization level.
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 the delay, looks good to me 👍. Thank you @mgeplf and @sanjayankur31 for the improvements!
Fixes an assertion error thrown when
_GLIBCXX_ASSERTIONS
is defined, and enables the option by default