Skip to content

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

Closed
kszucs wants to merge 23 commits into
apache:masterfrom
kszucs:add-kernel
Closed

ARROW-9042: [C++] Add Subtract and Multiply arithmetic kernels with wrap-around behavior#7341
kszucs wants to merge 23 commits into
apache:masterfrom
kszucs:add-kernel

Conversation

@kszucs

@kszucskszucs commented Jun 3, 2020

Copy link
Copy Markdown
Member

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

Comment threadcpp/src/arrow/compute/kernels/codegen_internal.h Outdated
Comment threadcpp/src/arrow/compute/kernels/scalar_arithmetic.cc Outdated
Comment threadcpp/src/arrow/compute/kernels/scalar_arithmetic.cc Outdated
@github-actions

Copy link
Copy Markdown

@bkietz

Copy link
Copy Markdown
Member

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

kszucs commented Jun 3, 2020

Copy link
Copy Markdown
MemberAuthor

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

wesm commented Jun 3, 2020

Copy link
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

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

@pitrou

Copy link
Copy Markdown
Member

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

wesm commented Jun 3, 2020

Copy link
Copy Markdown
Member

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

Comment threadcpp/src/arrow/compute/api_scalar.h Outdated
Comment threadcpp/src/arrow/compute/api_scalar.h Outdated
Comment threadcpp/src/arrow/compute/kernels/scalar_arithmetic.cc Outdated
Comment threadcpp/src/arrow/compute/kernels/scalar_arithmetic.cc Outdated
Comment threadcpp/src/arrow/compute/api_scalar.h Outdated
Comment threadcpp/src/arrow/compute/kernels/codegen_internal.h Outdated
@kszucs

Copy link
Copy Markdown
MemberAuthor

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

wesm commented Jun 4, 2020

Copy link
Copy Markdown
Member

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

@kszucs

Copy link
Copy Markdown
MemberAuthor

Just fixed it.

Copy link
Copy Markdown
MemberAuthor

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?

@kszucskszucs changed the title ARROW-9022: [C++][Compute] Make Add function safe for numeric limitsARROW-9042: [C++] Add Subtract and Multiply arithmetic kernels with wrap-around behaviorJun 5, 2020
@github-actions

Copy link
Copy Markdown

@kszucs

kszucs commented Jun 5, 2020

Copy link
Copy Markdown
MemberAuthor

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

@kszucs

Copy link
Copy Markdown
MemberAuthor

Nice! Thanks for upgrading it!

@bkietzbkietz closed this in 66bc8f0Jun 9, 2020
*out_data++ = Op::template Call<OUT, ARG0, ARG1>(ctx, *arg0_data++, *arg1_data++);
}
}
}

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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 @wesmCloses#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 freeto 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

@kszucs@bkietz@wesm@pitrou