-
Notifications
You must be signed in to change notification settings - Fork 163
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
perf: improve decimal read performance in CometVector #756
Conversation
(Updated) Micro Benchmark numbers -
Previously |
Oops, I did not see this earlier... related #758 |
} else if (!useDecimal128 && precision <= Decimal.MAX_LONG_DIGITS()) { | ||
return createDecimal(getLong(i), precision, scale); | ||
} else if (useDecimal128 && precision <= Decimal.MAX_LONG_DIGITS()) { | ||
return createDecimal(getLongFromDecimalBytes(getBinaryDecimal(i)), precision, scale); |
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: for readability, I think it is better not to repeat the precision <= Decimal.MAX_LONG_DIGITS()
expression
} else if (!useDecimal128 && precision <= Decimal.MAX_LONG_DIGITS()) { | |
return createDecimal(getLong(i), precision, scale); | |
} else if (useDecimal128 && precision <= Decimal.MAX_LONG_DIGITS()) { | |
return createDecimal(getLongFromDecimalBytes(getBinaryDecimal(i)), precision, scale); | |
} else if (precision <= Decimal.MAX_LONG_DIGITS()) { | |
if (useDecimal128) { | |
return createDecimal(getLongFromDecimalBytes(getBinaryDecimal(i)), precision, scale); | |
} else { | |
return createDecimal(getLong(i), precision, scale); | |
} |
System.arraycopy( | ||
DECIMAL_BYTES_ALL, i * DECIMAL_BYTE_WIDTH, DECIMAL_BYTES, 0, DECIMAL_BYTE_WIDTH); |
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.
Should this be copying to dest
rather than DECIMAL_BYTES
?
System.arraycopy( | |
DECIMAL_BYTES_ALL, i * DECIMAL_BYTE_WIDTH, DECIMAL_BYTES, 0, DECIMAL_BYTE_WIDTH); | |
System.arraycopy( | |
DECIMAL_BYTES_ALL, i * DECIMAL_BYTE_WIDTH, dest, 0, DECIMAL_BYTE_WIDTH); |
@andygrove @kazuyukitanimura, this is still in progress. There are ci failures and I may have to remove the part where this PR converts a 16 byte array to int/long. The conversion is not as simple as it looks (a glance at BigInteger code shows how complex it can be), and the code in this PR fails to convert negative values correctly. Including all the checks will take us back to the same performance as BigInteger. |
…l.MAX_LONG_DIGITS
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #756 +/- ##
============================================
+ Coverage 33.68% 33.78% +0.10%
- Complexity 822 871 +49
============================================
Files 111 112 +1
Lines 42671 42745 +74
Branches 9403 9436 +33
============================================
+ Hits 14372 14442 +70
+ Misses 25325 25324 -1
- Partials 2974 2979 +5 ☔ View full report in Codecov by Sentry. |
@kazuyukitanimura @andygrove After ci fixes, this PR only brings us up to 0.8x of Spark. Looking at another fix to close this gap, but in the meantime, this is good to review. |
// If the index is zero and DECIMAL_BYTES_ALL already has data, we have a new | ||
// batch of data in the vector's backing buffer. So read it again. |
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.
Could you expand on why i==0
means that have new data? I'm not entirely clear on why this is the case.
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 check is now unnecessary so I've removed it. For reference, ColumnReader.loadVector
specified the rules under which the vector may be reused for a new batch of data. When a new batch is read into the same vector, the underlying memory buffer is reused and there is no change in the buffer memory address of the value vector. The check for index==0
banked on the fact that the iterator reading the values will start from the beginning every time a new batch is read. There was a gotcha though. If the first N values were null the iterator would skip this method and call it for the first time with an index of N which could be non-zero.
The only safe way is to reset the buffer when a new batch is read (which is what this PR does now).
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 it possible to get new speed up numbers?
@@ -88,7 +90,11 @@ public Decimal getDecimal(int i, int precision, int scale) { | |||
if (!useDecimal128 && precision <= Decimal.MAX_INT_DIGITS() && type instanceof IntegerType) { | |||
return createDecimal(getInt(i), precision, scale); | |||
} else if (precision <= Decimal.MAX_LONG_DIGITS()) { | |||
return createDecimal(useDecimal128 ? getLongDecimal(i) : getLong(i), precision, scale); | |||
if (useDecimal128) { | |||
return createDecimal(getLongFromDecimalBytes(getBinaryDecimal(i)), precision, scale); |
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.
Hmm not sure how this is working with dictionary...
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.
CometDictionaryVector
overrides getBinaryDecimal
so we get the correct value in the buffer.
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 think getBinaryDecimal
calls decodeToBinary
that copies buffer... I am curious whether it may slows down things for dictionaries...
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 PR leaves CometDictionary.decodeToBinary
untouched (mainly because dictionary for decimals is a very rare case). However, CometDictionary.decodeToBinary
does the same copy that CometVector
was doing by calling CometVector.copyBinaryValues
which this PR improves, so one would expect better performance?
I haven't validated performance with dictionary values, though I have checked that the unit tests pass (ParquetReadSuite
covers dictionary encoded decimals).
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, I will look into this more to get better understanding.
The current getLongDecimal
does not call decodeToBinary
so I feel this change has more copies...
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.
Oh, right. I did not look into your getLongDecimal
change. That might be better. Let me also verify.
The original PR comment is updated with the latest benchmark numbers. |
@kazuyukitanimura looks like the |
@parthchandra I think the idea of getting all buffers at once may help for
|
I thought about that but the cost of creating BigInteger overshadowed the cost of getting the buffer from the vector. For high precision decimals, I feel we should focus on getting everything done in native.
I tried this. It's slower than your current implementation. ByteBuffer creation itself turned out to be expensive and I believe, ByteBuffer.getLong also ends up calling unsafe so I suspect the implementation might be shared. |
Which issue does this PR close?
Part of #679 and #670
Rationale for this change
Improves performance of reading decimals in Comet vector
What changes are included in this PR?
There are two changes
Decimal.MAX_LONG_DIGITS
we will read the values into a long rather than into a BigInteger/BigDecimalCaveat: the code now uses
num_elements_in_vector * size of element = 4K * 4 = 16
bytes more than previously.How are these changes tested?
Existing tests