Skip to content

Conversation

@JoshRosen
Copy link
Contributor

This patch modifies BytesToBytesMap.iterator() to iterate through records in the order that they appear in the data pages rather than iterating through the hashtable pointer arrays. This results in fewer random memory accesses, significantly improving performance for scan-and-copy operations.

This is possible because our data pages are laid out as sequences of [keyLength][data][valueLength][data] entries. In order to mark the end of a partially-filled data page, we write -1 as a special end-of-page length (BytesToByesMap supports empty/zero-length keys and values, which is why we had to use a negative length).

This patch incorporates / closes #5836.

@AmplabJenkins
Copy link

Merged build triggered.

@AmplabJenkins
Copy link

Merged build started.

@SparkQA
Copy link

SparkQA commented May 14, 2015

Test build #32737 has started for PR 6159 at commit 273b842.

@JoshRosen
Copy link
Contributor Author

Here's some benchmark code which builds a map with millions of records that have random 64-byte keys and values, then repeatedly iterates over the map and copies the keys and values into byte arrays: https://gist.github.com/JoshRosen/286b26494ab27e657051

For this benchmark, I saw a nearly 10x improvement as a result of this patch.

@SparkQA
Copy link

SparkQA commented May 14, 2015

Test build #32737 has finished for PR 6159 at commit 273b842.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@AmplabJenkins
Copy link

Merged build finished. Test PASSed.

@AmplabJenkins
Copy link

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/32737/
Test PASSed.

@JoshRosen
Copy link
Contributor Author

/cc @rxin for review.

@rxin
Copy link
Contributor

rxin commented May 17, 2015

@zsxwing can you take a look at this one as well?

@zsxwing
Copy link
Member

zsxwing commented May 18, 2015

@JoshRosen could you explain what will happen if oldCapacity == (1 << 30) in https://github.com/JoshRosen/spark/blob/SPARK-7251/unsafe/src/main/java/org/apache/spark/unsafe/map/BytesToBytesMap.java#L570 ? I think it will crash because of overflow. Right? It will pass -1 -2147483648 to allocate.

Copy link
Member

Choose a reason for hiding this comment

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

The current implementation does not follow the javadoc of Iterator: @throws NoSuchElementException if the iteration has no more elements. However, it may be fine since it's an internal API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we decide to do this, there's other places where we should also perform the same change. I'd like to defer this decision for now and deal with it in a follow up patch that makes the change more broadly.

@zsxwing
Copy link
Member

zsxwing commented May 18, 2015

How about make BytesToBytesMap implement Iterable<BytesToBytesMap.Location> so that it can be used in for-each?

Copy link
Member

Choose a reason for hiding this comment

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

Could you also add a test to iterate an empty BytesToBytesMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. I'll fold this into the emptyMap() test at the start of this file.

@JoshRosen
Copy link
Contributor Author

@zsxwing, nice catch RE: the risk of overflow when growing the map. This is a really hard-to-hit corner-case, but it's still worth fixing. I'll address this shortly.

@JoshRosen
Copy link
Contributor Author

I'm adding a test for the overflow and noticed something interesting: when we overflow to -2147483648 and pass that value to allocate, this case ends up being handled in Math.max in allocate(), so we end up allocating an array of size 64, which should end up causing problems in rehashing. It looks like the Math.max was a carryover from Reynold's original LongToLong map: https://github.com/rxin/jvm-unsafe-utils/blame/master/core/src/main/java/com/databricks/unsafe/util/LongToLongMap.java#L184. I'll strengthen the internal assertions to catch this.

@JoshRosen
Copy link
Contributor Author

Ah, I see now that it has to be at least 64 so that the division when sizing the bitset is safe. I'll leave a comment explaining this.

@JoshRosen
Copy link
Contributor Author

While fixing this, I found a units / sizing issue: our longArray can only contain Integer.MAX_VALUE elements and each map entry requires two entires in longArray, so our actual maximum capacity (# of supported map elements ) should be Integer.MAX_VALUE / 2. I'm fixing this and adding additional tests for the various boundary conditions near the maximum size.

@AmplabJenkins
Copy link

Merged build triggered.

@AmplabJenkins
Copy link

Merged build started.

@SparkQA
Copy link

SparkQA commented May 18, 2015

Test build #33030 has started for PR 6159 at commit bc4854b.

Copy link
Member

Choose a reason for hiding this comment

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

MAX_CAPACITY should be (1 << 30) - 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The hashtable has to be power-of-2-sized. Our long array can have at most (1 << 30) elements, since that's the largest power-of-2 that's less than Integer.MAX_VALUE, but we need two long array entries per record, so that gives us a maximum capacity of (1 << 29).

@SparkQA
Copy link

SparkQA commented May 19, 2015

Test build #33030 has finished for PR 6159 at commit bc4854b.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@AmplabJenkins
Copy link

Merged build finished. Test PASSed.

@AmplabJenkins
Copy link

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/33030/
Test PASSed.

@AmplabJenkins
Copy link

Merged build triggered.

@AmplabJenkins
Copy link

Merged build started.

@SparkQA
Copy link

SparkQA commented May 19, 2015

Test build #33100 has started for PR 6159 at commit 05bd90a.

@SparkQA
Copy link

SparkQA commented May 19, 2015

Test build #33100 has finished for PR 6159 at commit 05bd90a.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds the following public classes (experimental):
    • public class TaskMemoryManager

@AmplabJenkins
Copy link

Merged build finished. Test PASSed.

@AmplabJenkins
Copy link

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/33100/
Test PASSed.

@zsxwing
Copy link
Member

zsxwing commented May 19, 2015

LGTM

@JoshRosen
Copy link
Contributor Author

Thanks for the review. I'm going to merge this into master and branch-1.4 (1.4.0).

asfgit pushed a commit that referenced this pull request May 20, 2015
This patch modifies `BytesToBytesMap.iterator()` to iterate through records in the order that they appear in the data pages rather than iterating through the hashtable pointer arrays. This results in fewer random memory accesses, significantly improving performance for scan-and-copy operations.

This is possible because our data pages are laid out as sequences of `[keyLength][data][valueLength][data]` entries.  In order to mark the end of a partially-filled data page, we write `-1` as a special end-of-page length (BytesToByesMap supports empty/zero-length keys and values, which is why we had to use a negative length).

This patch incorporates / closes #5836.

Author: Josh Rosen <[email protected]>

Closes #6159 from JoshRosen/SPARK-7251 and squashes the following commits:

05bd90a [Josh Rosen] Compare capacity, not size, to MAX_CAPACITY
2a20d71 [Josh Rosen] Fix maximum BytesToBytesMap capacity
bc4854b [Josh Rosen] Guard against overflow when growing BytesToBytesMap
f5feadf [Josh Rosen] Add test for iterating over an empty map
273b842 [Josh Rosen] [SPARK-7251] Perform sequential scan when iterating over entries in BytesToBytesMap

(cherry picked from commit f2faa7a)
Signed-off-by: Josh Rosen <[email protected]>
@asfgit asfgit closed this in f2faa7a May 20, 2015
@JoshRosen JoshRosen deleted the SPARK-7251 branch May 26, 2015 03:40
jeanlyn pushed a commit to jeanlyn/spark that referenced this pull request May 28, 2015
This patch modifies `BytesToBytesMap.iterator()` to iterate through records in the order that they appear in the data pages rather than iterating through the hashtable pointer arrays. This results in fewer random memory accesses, significantly improving performance for scan-and-copy operations.

This is possible because our data pages are laid out as sequences of `[keyLength][data][valueLength][data]` entries.  In order to mark the end of a partially-filled data page, we write `-1` as a special end-of-page length (BytesToByesMap supports empty/zero-length keys and values, which is why we had to use a negative length).

This patch incorporates / closes apache#5836.

Author: Josh Rosen <[email protected]>

Closes apache#6159 from JoshRosen/SPARK-7251 and squashes the following commits:

05bd90a [Josh Rosen] Compare capacity, not size, to MAX_CAPACITY
2a20d71 [Josh Rosen] Fix maximum BytesToBytesMap capacity
bc4854b [Josh Rosen] Guard against overflow when growing BytesToBytesMap
f5feadf [Josh Rosen] Add test for iterating over an empty map
273b842 [Josh Rosen] [SPARK-7251] Perform sequential scan when iterating over entries in BytesToBytesMap
jeanlyn pushed a commit to jeanlyn/spark that referenced this pull request Jun 12, 2015
This patch modifies `BytesToBytesMap.iterator()` to iterate through records in the order that they appear in the data pages rather than iterating through the hashtable pointer arrays. This results in fewer random memory accesses, significantly improving performance for scan-and-copy operations.

This is possible because our data pages are laid out as sequences of `[keyLength][data][valueLength][data]` entries.  In order to mark the end of a partially-filled data page, we write `-1` as a special end-of-page length (BytesToByesMap supports empty/zero-length keys and values, which is why we had to use a negative length).

This patch incorporates / closes apache#5836.

Author: Josh Rosen <[email protected]>

Closes apache#6159 from JoshRosen/SPARK-7251 and squashes the following commits:

05bd90a [Josh Rosen] Compare capacity, not size, to MAX_CAPACITY
2a20d71 [Josh Rosen] Fix maximum BytesToBytesMap capacity
bc4854b [Josh Rosen] Guard against overflow when growing BytesToBytesMap
f5feadf [Josh Rosen] Add test for iterating over an empty map
273b842 [Josh Rosen] [SPARK-7251] Perform sequential scan when iterating over entries in BytesToBytesMap
nemccarthy pushed a commit to nemccarthy/spark that referenced this pull request Jun 19, 2015
This patch modifies `BytesToBytesMap.iterator()` to iterate through records in the order that they appear in the data pages rather than iterating through the hashtable pointer arrays. This results in fewer random memory accesses, significantly improving performance for scan-and-copy operations.

This is possible because our data pages are laid out as sequences of `[keyLength][data][valueLength][data]` entries.  In order to mark the end of a partially-filled data page, we write `-1` as a special end-of-page length (BytesToByesMap supports empty/zero-length keys and values, which is why we had to use a negative length).

This patch incorporates / closes apache#5836.

Author: Josh Rosen <[email protected]>

Closes apache#6159 from JoshRosen/SPARK-7251 and squashes the following commits:

05bd90a [Josh Rosen] Compare capacity, not size, to MAX_CAPACITY
2a20d71 [Josh Rosen] Fix maximum BytesToBytesMap capacity
bc4854b [Josh Rosen] Guard against overflow when growing BytesToBytesMap
f5feadf [Josh Rosen] Add test for iterating over an empty map
273b842 [Josh Rosen] [SPARK-7251] Perform sequential scan when iterating over entries in BytesToBytesMap
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.

5 participants