Skip to content

GH-20408: [Go] Implement Encode and Decode functions for REE - #34534

Merged
zeroshade merged 5 commits into
apache:mainfrom
zeroshade:go-ree-encode-decode
Mar 13, 2023
Merged

zeroshade merged 5 commits into
apache:mainfrom
zeroshade:go-ree-encode-decode

Conversation

@zeroshade

@zeroshade zeroshade commented Mar 10, 2023 •

Copy link
Copy Markdown
Member

What changes are included in this PR?

Adding new run_end_encode and run_end_decode functions to the Go Compute library.

Are these changes tested?

Yes

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #20408 has been automatically assigned in GitHub to PR creator.

@zeroshade

Copy link
Copy Markdown
Member Author

CC @felipecrv so we can make sure that I didn't miss anything and that this is on par with the C++ impl

Comment thread go/arrow/compute/vector_run_ends.go Resolved
inputOffset: input.Offset,
inputValidity: input.Buffers[0].Buf,
inputValues: input.Buffers[2].Buf,
offsetValues: exec.GetSpanOffsets[O](input, 1),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this apply input.Offset? That makes it a bit too different from the other buffers pointers in this struct.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

one complexity of binary arrays is that the offset values are always absolute offsets into the raw data. So in order for everything to work correctly, the slice for the raw values (inputValues) can't be shifted by the offset. In addition, you can't easily shift the validity bitmap in a predictable way because it's a bitmap and you aren't guaranteed the offset is easily divisible by 8. So those can't utilize the offset built in.

However, exec.GetSpanOffsets already accounts for the offset when creating the typed slice of offsets allowing the simple change of just not needing to apply the offset to the readOffset in the loop for var binary arrays.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TIL. Thanks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

the same is also true for exec.GetSpanValues

The difference between GetSpanValues and GetSpanOffsets is that GetSpanOffsets adds the expected +1 to the length that offsets should have. so len(exec.GetSpanOffsets(....)) == span.Len+1

Also, exec.GetSpanOffsets doesn't do the nil check that GetSpanValues does.

for i, runEnd := range de.inputRunEnds[de.inputPhysicalOffset:] {
runLength, prevRunEnd = int64(runEnd-prevRunEnd), runEnd
// if this run is a null, clear the bits and update writeOffset
if len(de.inputValidity) != 0 && bitutil.BitIsNotSet(de.inputValidity, int(de.inputValueOffset+de.inputPhysicalOffset)+i) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

inputValueOffset could be just inputOffset here, right? It refers to the logical offset of the parent array. (?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

  • de.inputOffset is the logical offset of the parent array
  • de.inputPhysicalOffset is the physical offset into the values and runs children arrays
  • de.inputValueOffset is the internal offset of the values child array

For example, let's say you have the array [1, 2, 3, 4, 5]. Then let's take a slice of that, [2, 3, 4] and use that as our values for a run-end-encoded array. Finally let's add a logical offset to the REE array.

To make up some runs, that gives us the final REE array of:

  • Parent: logical offset = 20, len = 30
    • Runs: offset = 0, len = 3, [15, 30, 50]
    • Values: offset = 1, len = 3, [1, 2, 3, 4, 5]

To get the proper values for the runs, we first find the physical offset (1) from the logical offset. Then when we index into the values child, we have to also apply the child's offset (1). Giving us the first value of 3 (index = 2). This of course also applies to determining the bitmap validity for nulls.

Does that make sense? This is also tested in the unit tests.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh right. I encapsulated much of this in a forward iterator in the C++ version and had forgotten there are 3 offset variables to address a physical value sometimes :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yea, Go doesn't really have iterators so there's just the merged runs iterator thing, but that's designed for iterating across 2 REE arrays simultaneously and getting the runs that line up rather than just iterating a single REE array. Maybe one day Go will provide a way to hook into the for ... := range construction for custom types lol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

so there's just the merged runs iterator thing, but that's designed for iterating across 2 REE arrays simultaneously

I took that thigk apart in the C++ version and made a similar class that takes a single ArraySpan and allows iterating on the runs. It's not completely C++ iterator compatible, so I think a similar thing could be achieved with Go. Perhaps Go doesn't inline as aggressively and it wouldn't be as efficient.

@zeroshade
zeroshade merged commit eaa3100 into apache:main Mar 13, 2023
@zeroshade
zeroshade deleted the go-ree-encode-decode branch March 13, 2023 17:04
@ursabot

ursabot commented Mar 14, 2023

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = 9c218ab and contender = eaa3100. eaa3100 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Finished ⬇️0.36% ⬆️0.0%] test-mac-arm
[Finished ⬇️0.0% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.53% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] eaa3100a ec2-t3-xlarge-us-east-2
[Finished] eaa3100a test-mac-arm
[Finished] eaa3100a ursa-i9-9960x
[Finished] eaa3100a ursa-thinkcentre-m75q
[Finished] 9c218abb ec2-t3-xlarge-us-east-2
[Finished] 9c218abb test-mac-arm
[Finished] 9c218abb ursa-i9-9960x
[Finished] 9c218abb ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Go] Implement Encode and Decode functions for REE

3 participants