Skip to content
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

ARROW-9042: [C++] Add Subtract and Multiply arithmetic kernels with wrap-around behavior #7341

Closed
wants to merge 23 commits into from

Conversation

kszucs
Copy link
Member

@kszucs kszucs commented Jun 3, 2020

Currently the output type of the Add function is identical with the argument types which makes it unsafe to add numeric limit values, so instead of using (int8, int8) -> int8 signature we should use (int8, int8) -> int16.

  • avoid undefined behaviour caused by signed integer overflow by casting to unsigned counterparts before operation
  • added subtract and multiply kernels (multiply required special handling for uint16 types to avoid casting the operands to signed int32 types)
  • test case parametrization supporting different argument and output types (not used for the current tests cases, but will be useful for the upcoming arithmetic kernels)

follow-ups:

  • variants that raise on overflow
  • AssertArrayAlmostEqual for floating point comparisons

@github-actions
Copy link

github-actions bot commented Jun 3, 2020

@bkietz
Copy link
Member

bkietz commented Jun 3, 2020

we should use (int8, int8) -> int16

I'm not sure that's desirable. For one thing this leads to inconsistent handling of 64 bit integer types, which are currently allowed to overflow (NB: that means this kernel includes undefined behavior for int64).

There are a few other approaches we could take (ordered by personal preference):

  • define explicit overflow behavior for signed integer operands (for example if we declared that add(i8(a), i8(b)) will always be equivalent to i8(i16(a) + i16(b)) then we could instantiate only unsigned addition kernels)
  • raise an error on signed overflow
  • provide ArithmeticOptions::overflow_behavior and allow users to choose between these
  • require users to pass arguments which will not overflow

@wesm ?

This is probably nuanced enough to merit a mailing list discussion.

@kszucs
Copy link
Member Author

kszucs commented Jun 3, 2020

This is probably nuanced enough to merit a mailing list discussion.

Certainly.

What kind of overflow strategies would could we have for other functions with higher probability of overflowing like product?

@wesm
Copy link
Member

wesm commented Jun 3, 2020

Per the mailing list discussion, I think we should apply the strategies used by open source analytic DBMS that we have access to and not think too hard about it:

  • Do signed arithmetic as unsigned to prevent UB
  • Do not promote small integers (except perhaps in multiplication, what do the DBMSes do?)
  • Do not check for overflows

@kszucs
Copy link
Member Author

kszucs commented Jun 3, 2020

I'll update the PR as discussed, and defer the implicit promotions to a follow-up PR.

@pitrou
Copy link
Member

pitrou commented Jun 3, 2020

As I said on the ML, I'm -1 on implicit promotion. We may discuss whether overflow should be detected or not. But trying to make the output type "big enough" is a can of worms.

@wesm
Copy link
Member

wesm commented Jun 3, 2020

Agreed. Implicit casts or type promotions is something that is typically negotiated by the orchestration/planning layer 1 to 2 levels above the kernels.

cpp/src/arrow/compute/api_scalar.h Outdated Show resolved Hide resolved
cpp/src/arrow/compute/api_scalar.h Outdated Show resolved Hide resolved
cpp/src/arrow/compute/kernels/scalar_arithmetic.cc Outdated Show resolved Hide resolved
@kszucs
Copy link
Member Author

kszucs commented Jun 4, 2020

The (uint16 * uint16) operation triggers the following ubsan error:

runtime error: signed integer overflow: 65535 * 65535 cannot be represented in type 'int'

According to this SO post we might need a special case for uint16 (or perhaps it depends on the arch) multiplication.

@wesm
Copy link
Member

wesm commented Jun 4, 2020

We could also simply not generate multiply kernels for 8- and 16-byte integer types.

@kszucs
Copy link
Member Author

kszucs commented Jun 5, 2020

Just fixed it.


this->AssertAdd("[3, 2, 6]", "[1, 0, 2]", "[4, 2, 8]");
// this->AssertBinop(arrow::compute::Subtract,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The array equality check fails despite that after printing out both sides the values are the same. I'm not sure why, perhaps a float precision problem?

@kszucs kszucs changed the title ARROW-9022: [C++][Compute] Make Add function safe for numeric limits ARROW-9042: [C++] Add Subtract and Multiply arithmetic kernels with wrap-around behavior Jun 5, 2020
@github-actions
Copy link

github-actions bot commented Jun 5, 2020

@kszucs
Copy link
Member Author

kszucs commented Jun 5, 2020

@bkietz I think it is ready for a review now, the build failures are present on the master branch.

@kszucs
Copy link
Member Author

kszucs commented Jun 8, 2020

Nice! Thanks for upgrading it!

@bkietz bkietz closed this in 66bc8f0 Jun 9, 2020
*out_data++ = Op::template Call<OUT, ARG0, ARG1>(ctx, *arg0_data++, *arg1_data++);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to remove this, you absolutely must write benchmarks to show that the more general version is not slower.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing a benchmark, the jira to track it https://issues.apache.org/jira/browse/ARROW-9079

wesm added a commit that referenced this pull request Jun 15, 2020
Quickly wanted to add a benchmark for the `Add` function to verify that no significant regressions were introduced by #7341

Before:
```
---------------------------------------------------------------------------------------
Benchmark                                Time           CPU Iterations UserCounters...
---------------------------------------------------------------------------------------
AddArrayArrayKernel/32768/10000         18 us         18 us      35892 null_percent=0.01 size=32.768k   1.67854GB/s
AddArrayArrayKernel/32768/100           19 us         19 us      37540 null_percent=1 size=32.768k   1.61941GB/s
AddArrayArrayKernel/32768/10            20 us         20 us      37049 null_percent=10 size=32.768k   1.55599GB/s
AddArrayArrayKernel/32768/2             20 us         20 us      35394 null_percent=50 size=32.768k   1.54512GB/s
AddArrayArrayKernel/32768/1             19 us         19 us      37901 null_percent=100 size=32.768k   1.63153GB/s
```

After:
```
---------------------------------------------------------------------------------------
Benchmark                                Time           CPU Iterations UserCounters...
---------------------------------------------------------------------------------------
AddArrayArrayKernel/32768/10000         19 us         19 us      36704 null_percent=0.01 size=32.768k   1.64619GB/s
AddArrayArrayKernel/32768/100           18 us         18 us      37194 null_percent=1 size=32.768k   1.67588GB/s
AddArrayArrayKernel/32768/10            18 us         18 us      36341 null_percent=10 size=32.768k   1.65205GB/s
AddArrayArrayKernel/32768/2             18 us         18 us      37502 null_percent=50 size=32.768k     1.662GB/s
AddArrayArrayKernel/32768/1             18 us         18 us      38622 null_percent=100 size=32.768k   1.66593GB/s
```

cc @wesm

Closes #7417 from kszucs/ARROW-9079

Lead-authored-by: Krisztián Szűcs <szucs.krisztian@gmail.com>
Co-authored-by: Benjamin Kietzman <bengilgit@gmail.com>
Co-authored-by: Wes McKinney <wesm@apache.org>
Signed-off-by: Wes McKinney <wesm@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants