-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 equality check for simple floating types in RowContainer #7780
Conversation
✅ Deploy Preview for meta-velox canceled.
|
bfb6fe7
to
99a2445
Compare
99a2445
to
307dd1a
Compare
307dd1a
to
2ef2b29
Compare
2ef2b29
to
e8550cb
Compare
auto rowContainer = makeRowContainer({type}, {type}, false); | ||
int numRows = values->size(); | ||
DecodedVector decodedWithNulls(*values); | ||
auto rows = storeRows(decodedWithNulls, numRows, *rowContainer); |
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.
It looks like the test loops are very similar. Can you abstract a common function for it.
e8550cb
to
8259968
Compare
8259968
to
a4d1fba
Compare
2db2153
to
4fe7aff
Compare
@aditi-pandit @mbasmanova Please review. I addressed @aditi-pandit earlier comment. Thanks! |
@@ -466,8 +466,40 @@ class RowContainerTest : public exec::test::RowContainerTestBase { | |||
} | |||
} | |||
|
|||
template <typename T, bool mayHaveNulls> |
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.
Nit : Use of "mayHaveNulls" sounds ambiguous. Could you use just hasNulls
?
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 used the same name as used for the equals
function. The template doesn't indicate if the input vector has nulls or not - it actually always has nulls because I didn't want to generate two of them in the caller. Instead, it is passed to the equals function to expect nulls or not. Thus, if this is false
, then null values must be removed from the input vector.
Perhaps it should be named equalsCanHandleNulls
or something?
@@ -466,8 +466,40 @@ class RowContainerTest : public exec::test::RowContainerTestBase { | |||
} | |||
} | |||
|
|||
template <typename T, bool mayHaveNulls> |
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.
Don't see the use of template type 'T' in the method. Is something missing ?
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.
No, you are right. It is not needed. Which also means I can remove it from the other test as well. It is used in callers to distinguish the double/float type but it is not needed at this level anymore.
4fe7aff
to
d7e688f
Compare
d7e688f
to
79843e5
Compare
|
||
int32_t index{0}; | ||
for (auto row : rows) { | ||
ASSERT_TRUE(rowContainer->equals<canHandleNulls>( |
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 should add some tests for rowContainer->equals returning false as well.
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.
What kind of values do you think of using to test the evaluation to false?
This test uses the edge values for floating points so compare them against each other? Or some random floating point values?
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.
Yeah, I was thinking one edge against another like say NaN with max/min and also will some regular random floating point values.
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, I added some more tests to test random and edge values against NaN values.
} | ||
|
||
auto numRows = values->size(); | ||
auto valuesSlice = values->slice(numNulls, numRows - numNulls); |
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.
Want to make sure I dont misunderstand this - but say you have 10 rows and 1 null, then you take the offset from 2nd to last. Are the nulls going to be from 0 to numNulls ?
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, the test uses a specific input vector of edge values where the initial row is a NULL. It then uses a subset to specifically test the rowContainer->equals<false>
where no NULL values can be part of the input.
Practically, that means numNulls == 1
.
An example for an input vector is:
velox/velox/exec/tests/RowContainerTest.cpp
Line 505 in 7f9914b
auto values = makeNullableFlatVector<T>( |
There are tests for the various row types and they always have a specific input to test the edge cases.
79843e5
to
1a1508d
Compare
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.
Tests look good. Thanks @czentgr
template <typename T, typename V> | ||
void testOrderAndNullsFirstVariations( | ||
template <bool canHandleNulls> | ||
void testRowContainerEqualAPI( |
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.
Nit : Spelling "Equals"
@mbasmanova : PTAL. |
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.
@bikramSingh91 Thank you for your detailed review and comments!
I addressed them. Please take a look.
for (auto row : rows) { | ||
ASSERT_EQ( | ||
expected->asFlatVector<bool>()->valueAt(index), | ||
rowContainer->equals<canHandleNulls>( |
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.
That's a good point.
while (numRows) { | ||
auto value = folly::Random::randDouble(min, max, gen); | ||
rawData.push_back(value); | ||
if (value == min || value == lowest || value == max) { |
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 correct. We always compare to nan
which any generated value or edge value should be false.
@bikramSingh91 has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
Hi @czentgr, this change looks good to me. But I wonder why the PR summary says that this change affects functions such as array_distinct and array_max? I saw the change affects RowContainer, but array_distinct and array_max do not use RowContainer, right? |
@kagamiori You are correct. The functions don't operate on the row container and the statement in the commit/PR is incorrect. I took a closer look at the functions and turns out there are more problems with NaN that aren't addressed by this fix. For example, results of
Java
The same problem exists for array_sort_desc. I also think there is a problem with
|
4ad4331
to
77a66c7
Compare
Hi @czentgr, thank you for updating the PR summary. We have recently noticed the NaN comparison issue in Velox functions too. (See #8738 and #8690) In fact, tracing back the behavior of NaN handling, we found that Presto has inconsistent NaN behaviors within and among functions too. There are two github issues in the Presto repo related to this problem: prestodb/presto#21877 and prestodb/presto#21936. Our current plan is to let Presto clarify (or fix) and document the NaN behavior of functions and then follow Presto in Velox. Please take a look at those github issues if you're interested. |
@bikramSingh91 has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@bikramSingh91 Would you please be able to tell me on the Linter warnings? I would like to fix them and also rebase the PR again. Did I add new warnings in this PR? |
@kagamiori Thank you for the found issues. I have a documentation PR for Velox to clarify the NaN behavior there. This is adopted from the Spark behavior and makes the most sense and provides consistency. Presumably, Presto should follow this as well: #7237. |
Hi @czentgr, @bikramSingh91 is out of office this week. The internal linter warnings come from the fact that the newly added methods in RowContainerTest.cpp defines a set of variables through TEST_FLOATING_TYPE_LIMIT_VARIABLES, but not all the defined variables are used in these methods (i.e., warning of unused variable). |
while (numRows) { | ||
auto value = folly::Random::randDouble(min, max, gen); | ||
// Intersperse nan values. | ||
if (static_cast<int64_t>(std::fmod(value, 3.0)) == 0) { | ||
rawData.push_back(nan); | ||
rawExpected.push_back(true); | ||
} else { | ||
rawData.push_back(value); | ||
rawExpected.push_back(false); | ||
} | ||
--numRows; | ||
} |
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.
Looks like this piece of code implicitly assumes that the other vector to be compared against the result of this method is an all-nan vector. But this assumption is not told in the name of this method or any comment. Could we add some comments to this method explaining what it does?
const VectorPtr& lhs, | ||
const VectorPtr& rhs, | ||
const std::vector<bool>& rawExpected) { | ||
TEST_FLOATING_TYPE_LIMIT_VARIABLES; |
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.
Is this macro not needed in this method?
int32_t numRows, | ||
std::vector<std::optional<T>>& rawData, | ||
std::vector<bool>& rawExpected) { | ||
TEST_FLOATING_TYPE_LIMIT_VARIABLES; |
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 macro results in a lot of warnings of unusued variables. Can you replace it with something like : auto [max, min, _ , , , _] = FLOATING_TYPE_LIMIT_VARIABLES();
Where the function returns a const std::tuple and you only give names to the ones you need ?
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 tried to use the tuple approach. The issue is that if you use more than one _
it complains because the identifier repeats. It doesn't exclude it. And from posts I read there should be warnings for _
as well as it is simply just a name for one of the returned entries.
Example:
/Users/czentgr/gitspace/velox/velox/exec/tests/RowContainerTest.cpp:630:25: error: redefinition of '_'
const auto [nan, _, _, _, _] = getTestFloatingTypeLimitVariables<T>();
^
I turned on-Wunused-variables
to see what the compiler will do and on macOS (clang) it doesn't trigger the error if the entries were named but not used. However, the linter might still complain.
In most functions all the special values should be used except in a few where only NaN is used. So I will just not use the macro there. I experimented with a templated struct instead of the macro but this also causes more changes.
2441348
to
cbe3d03
Compare
@kagamiori has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
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.
LGTM. Thank you for helping address the linter warning.
Hi @czentgr, could you rebase this PR onto the latest main? It's required for committing the code. Thanks! |
The row container implementation for equalsNoNulls and equalsWithNulls contained a bug: 1. Incorrect equals check for floating point types when NaN values are used. 2. Refactor to use SimpleVector::comparePrimitiveAsc in RowContainer and ContainerRowSerde for a common comparison function. 3. Change static SimpleVector::comparePrimitiveAsc to be static inline to reduce function call overhead in this expanded usage. This is a continuation of PR facebookincubator#5833 which addressed floating point comparisons for complex types. Affected operators: FilterProject, TopN, TopNRowNumber, OrderBy, MergeExchange, LocalMerge, HashProbe, NestedLoopJoinProbe The lists may not be complete.
cbe3d03
to
59e7e4f
Compare
@kagamiori done. Thanks! |
@kagamiori has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@kagamiori merged this pull request in 451db90. |
…kincubator#7780) Summary: The row container implementation for equalsNoNulls and equalsWithNulls contained a bug: 1. Incorrect equals check for floating point types when NaN values are used. 2. Refactor to use SimpleVector::comparePrimitiveAsc in RowContainer and ContainerRowSerde for a common comparison function. 3. Change static SimpleVector::comparePrimitiveAsc to be static inline to reduce function call overhead in this expanded usage. This is a continuation of PR facebookincubator#5833 which addressed floating point comparisons for complex types. Affected operators: FilterProject, TopN, TopNRowNumber, OrderBy, MergeExchange, LocalMerge, HashProbe, NestedLoopJoinProbe The lists may not be complete. Pull Request resolved: facebookincubator#7780 Reviewed By: Yuhta Differential Revision: D54141907 Pulled By: kagamiori fbshipit-source-id: 0306cfaffd4d486a0b72f6e6b659b40b2d66688f
The row container implementation for
equalsNoNulls and equalsWithNulls contained a bug:
This is a continuation of PR #5833 which addressed floating point comparisons for complex types.
Affected operators:
FilterProject, TopN, TopNRowNumber, OrderBy, MergeExchange, LocalMerge, HashProbe, NestedLoopJoinProbe
The lists may not be complete.