{"action":"created","comment":{"url":"https://api.github.com/repos/apache/spark/pulls/comments/29817569","id":29817569,"diff_hunk":"@@ -0,0 +1,105 @@\n+/*\n+ * Licensed to the Apache Software Foundation (ASF) under one or more\n+ * contributor license agreements.  See the NOTICE file distributed with\n+ * this work for additional information regarding copyright ownership.\n+ * The ASF licenses this file to You under the Apache License, Version 2.0\n+ * (the \"License\"); you may not use this file except in compliance with\n+ * the License.  You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package org.apache.spark.shuffle.unsafe;\n+\n+import java.util.Comparator;\n+\n+import org.apache.spark.util.collection.Sorter;\n+\n+public final class UnsafeShuffleSorter {\n+\n+  private final Sorter<PackedRecordPointer, long[]> sorter;\n+  private final Comparator<PackedRecordPointer> sortComparator;\n+\n+  private long[] sortBuffer;\n+\n+  /**\n+   * The position in the sort buffer where new records can be inserted.\n+   */\n+  private int sortBufferInsertPosition = 0;\n+\n+  public UnsafeShuffleSorter(int initialSize) {\n+    assert (initialSize > 0);\n+    this.sortBuffer = new long[initialSize];\n+    this.sorter =\n+      new Sorter<PackedRecordPointer, long[]>(UnsafeShuffleSortDataFormat.INSTANCE);\n+    this.sortComparator = new Comparator<PackedRecordPointer>() {\n+      @Override\n+      public int compare(PackedRecordPointer left, PackedRecordPointer right) {\n+        return left.getPartitionId() - right.getPartitionId();","path":"core/src/main/java/org/apache/spark/shuffle/unsafe/UnsafeShuffleSorter.java","position":44,"original_position":44,"commit_id":"b67441239aa80acf78a863868c31405ed9bea8d6","original_commit_id":"b67441239aa80acf78a863868c31405ed9bea8d6","user":{"login":"JoshRosen","id":50748,"avatar_url":"https://avatars.githubusercontent.com/u/50748?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshRosen","html_url":"https://github.com/JoshRosen","followers_url":"https://api.github.com/users/JoshRosen/followers","following_url":"https://api.github.com/users/JoshRosen/following{/other_user}","gists_url":"https://api.github.com/users/JoshRosen/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshRosen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshRosen/subscriptions","organizations_url":"https://api.github.com/users/JoshRosen/orgs","repos_url":"https://api.github.com/users/JoshRosen/repos","events_url":"https://api.github.com/users/JoshRosen/events{/privacy}","received_events_url":"https://api.github.com/users/JoshRosen/received_events","type":"User","site_admin":false},"body":"`getPartitionId()` returns an integer, so isn't this immune from overflow?  There are overflow concerns that are internal to `PackedRecordPointer`, such as the fact that we have an upper limit of ~10 million partitions.  Would it be okay if I commented on those overflow issues inside of `PackedRecordPointer` and left this line unchanged?","created_at":"2015-05-07T01:06:09Z","updated_at":"2015-05-07T01:06:09Z","html_url":"https://github.com/apache/spark/pull/5868#discussion_r29817569","pull_request_url":"https://api.github.com/repos/apache/spark/pulls/5868","_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/comments/29817569"},"html":{"href":"https://github.com/apache/spark/pull/5868#discussion_r29817569"},"pull_request":{"href":"https://api.github.com/repos/apache/spark/pulls/5868"}}},"pull_request":{"url":"https://api.github.com/repos/apache/spark/pulls/5868","id":34592881,"html_url":"https://github.com/apache/spark/pull/5868","diff_url":"https://github.com/apache/spark/pull/5868.diff","patch_url":"https://github.com/apache/spark/pull/5868.patch","issue_url":"https://api.github.com/repos/apache/spark/issues/5868","number":5868,"state":"open","locked":false,"title":"[WIP][SPARK-7081]Faster sort-based shuffle path using binary processing cache-aware sort","user":{"login":"JoshRosen","id":50748,"avatar_url":"https://avatars.githubusercontent.com/u/50748?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshRosen","html_url":"https://github.com/JoshRosen","followers_url":"https://api.github.com/users/JoshRosen/followers","following_url":"https://api.github.com/users/JoshRosen/following{/other_user}","gists_url":"https://api.github.com/users/JoshRosen/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshRosen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshRosen/subscriptions","organizations_url":"https://api.github.com/users/JoshRosen/orgs","repos_url":"https://api.github.com/users/JoshRosen/repos","events_url":"https://api.github.com/users/JoshRosen/events{/privacy}","received_events_url":"https://api.github.com/users/JoshRosen/received_events","type":"User","site_admin":false},"body":"This patch introduces a new shuffle manager that enhances the existing sort-based shuffle with a new cache-friendly sort algorithm that operates directly on binary data. The goals of this patch are to lower memory usage and Java object overheads during shuffle and to speed up sorting. It also lays groundwork for follow-up patches that will enable end-to-end processing of serialized records.\r\n\r\nThe new shuffle manager, `UnsafeShuffleManager`, can be enabled by setting `spark.shuffle.manager=unsafe` in SparkConf. The new shuffle write path in this manager will only be used for shuffles in which there is no specified key ordering, no aggregation, and for which a supported serializer is used. In cases where these conditions are not met, `UnsafeShuffleManager` will automatically delegate to the old `SortShuffleManager`.\r\n\r\nThis patch uses a specialized external sorting implementation. By using a specialized sorter, we can benefit from several performance optimizations that would be difficult to implement in a more general-purpose sorter:\r\n\r\n- We may not need a full 32-bits to record the partition id; since shuffles are unlikely to have more than, say, 10 million partitions, we can use fewer bits to encode the partition id, allowing us to pack it into a single word that uses the remaining bits to store the record pointer (exploiting the fact that we can choose a reasonable upper bound on our addressable memory (say 1 terabyte) and can save address bits due to word-alignment). This should give us excellent cache-locality benefits, since the sort array will only require 8 bytes of space per record.\r\n- Because sort-shuffle's sort keys are partition ids, we can expect to encounter long runs of records with the same sort key in the sorted file; this may not be the case in a more general-purpose sort, where there might be cases in which all of the sort keys are distinct. As a result, this allows external sort to use a specialized merge procedure that operates on runs of records with the same key rather than individual records.\r\n- Ignoring the spilling/merging case for a moment, the sort shuffle writer only needs to know individual records' lengths during the in-memory sort: we need to know a record's length in order to copy it to the sorted output file, but once a partition's records are adjacent in the output file we no longer need to know their individual lengths. This is a consequence of the fact that the sorted data will be consumed by a serializer that knows how to identify record boundaries, building on our assumption that the serializer supports reordering of serialized records in its serialization stream.\r\n- If we go further and assume that our IO compression codec supports concatenation of compressed data (which Snappy seems to support), then we can implement an IO-efficient merging procedure for external sort. If we store an index file for each spill file, identifying the offsets of each partition within the sorted file, then the merge sort procedure can simply traverse these indices and concatenate the partitions' serialized data without interpreting it. This would let us use methods like transferTo to implement copying without requiring data to be buffered in the JVM, sidestepping the complexity of managing IO buffers during the merge.\r\n\r\nThe shuffle read path is unchanged.\r\n\r\nThis patch is similar to [SPARK-4550](http://issues.apache.org/jira/browse/SPARK-4550) / #4450 but uses a slightly different implementation. The `unsafe`-based implementation featured in this patch lays the groundwork for followup patches that will enable sorting to operate on serialized data pages that will be prepared by Spark SQL's new `unsafe` operators (such as the new aggregation operator introduced in #5725).\r\n\r\n### Supported serializers\r\n\r\nBecause this sort operates on serialized records, it places certain requirements on the record serialization format. Specifically, it requires a _relocatability_ property in which records in a serialized stream of records can be re-ordered by reordering their serialized representations.  See #5924 for a patch that extends the Serializer API to let us determine whether this property holds.\r\n\r\n### Future work\r\n\r\nThere are several tasks that build upon this patch, which will be left to future work:\r\n\r\n- [SPARK-7271](https://issues.apache.org/jira/browse/SPARK-7271) Redesign / extend the shuffle interfaces to accept binary data as input. The goal here is to let us bypass serialization steps in cases where the sort input is produced by an operator that operates directly on binary data.\r\n- Extension / redesign of the `Serializer` API. We can add new methods which allow serializers to determine the size requirements for serializing objects and for serializing objects directly to a specified memory address (similar to how `UnsafeRowConverter` works in Spark SQL).\r\n  \r\n**TODOs for this patch**:\r\n\r\n- More test cases:\r\n  - [ ] Property-based / fuzz tests for sorter.\r\n  - [ ] Test that checks that counters like `numRecordsWritten`, etc., are properly updated.\r\n  - [ ] After #5948 is merged, update SQL's Exchange operator to also avoid copying when this new shuffle manager is used.\r\n\r\n<!-- Reviewable:start -->\r\n[<img src=\"https://reviewable.io/review_button.png\" height=40 alt=\"Review on Reviewable\"/>](https://reviewable.io/reviews/apache/spark/5868)\r\n<!-- Reviewable:end -->\r\n","created_at":"2015-05-02T22:26:37Z","updated_at":"2015-05-07T01:06:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cbcc62e1a80f02822a94cb026a0f2bc3466f24c6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/spark/pulls/5868/commits","review_comments_url":"https://api.github.com/repos/apache/spark/pulls/5868/comments","review_comment_url":"https://api.github.com/repos/apache/spark/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/spark/issues/5868/comments","statuses_url":"https://api.github.com/repos/apache/spark/statuses/b67441239aa80acf78a863868c31405ed9bea8d6","head":{"label":"JoshRosen:unsafe-sort","ref":"unsafe-sort","sha":"b67441239aa80acf78a863868c31405ed9bea8d6","user":{"login":"JoshRosen","id":50748,"avatar_url":"https://avatars.githubusercontent.com/u/50748?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshRosen","html_url":"https://github.com/JoshRosen","followers_url":"https://api.github.com/users/JoshRosen/followers","following_url":"https://api.github.com/users/JoshRosen/following{/other_user}","gists_url":"https://api.github.com/users/JoshRosen/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshRosen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshRosen/subscriptions","organizations_url":"https://api.github.com/users/JoshRosen/orgs","repos_url":"https://api.github.com/users/JoshRosen/repos","events_url":"https://api.github.com/users/JoshRosen/events{/privacy}","received_events_url":"https://api.github.com/users/JoshRosen/received_events","type":"User","site_admin":false},"repo":{"id":22238710,"name":"spark","full_name":"JoshRosen/spark","owner":{"login":"JoshRosen","id":50748,"avatar_url":"https://avatars.githubusercontent.com/u/50748?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshRosen","html_url":"https://github.com/JoshRosen","followers_url":"https://api.github.com/users/JoshRosen/followers","following_url":"https://api.github.com/users/JoshRosen/following{/other_user}","gists_url":"https://api.github.com/users/JoshRosen/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshRosen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshRosen/subscriptions","organizations_url":"https://api.github.com/users/JoshRosen/orgs","repos_url":"https://api.github.com/users/JoshRosen/repos","events_url":"https://api.github.com/users/JoshRosen/events{/privacy}","received_events_url":"https://api.github.com/users/JoshRosen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/JoshRosen/spark","description":"Mirror of Apache Spark","fork":true,"url":"https://api.github.com/repos/JoshRosen/spark","forks_url":"https://api.github.com/repos/JoshRosen/spark/forks","keys_url":"https://api.github.com/repos/JoshRosen/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/JoshRosen/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/JoshRosen/spark/teams","hooks_url":"https://api.github.com/repos/JoshRosen/spark/hooks","issue_events_url":"https://api.github.com/repos/JoshRosen/spark/issues/events{/number}","events_url":"https://api.github.com/repos/JoshRosen/spark/events","assignees_url":"https://api.github.com/repos/JoshRosen/spark/assignees{/user}","branches_url":"https://api.github.com/repos/JoshRosen/spark/branches{/branch}","tags_url":"https://api.github.com/repos/JoshRosen/spark/tags","blobs_url":"https://api.github.com/repos/JoshRosen/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/JoshRosen/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/JoshRosen/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/JoshRosen/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/JoshRosen/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/JoshRosen/spark/languages","stargazers_url":"https://api.github.com/repos/JoshRosen/spark/stargazers","contributors_url":"https://api.github.com/repos/JoshRosen/spark/contributors","subscribers_url":"https://api.github.com/repos/JoshRosen/spark/subscribers","subscription_url":"https://api.github.com/repos/JoshRosen/spark/subscription","commits_url":"https://api.github.com/repos/JoshRosen/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/JoshRosen/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/JoshRosen/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/JoshRosen/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/JoshRosen/spark/contents/{+path}","compare_url":"https://api.github.com/repos/JoshRosen/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/JoshRosen/spark/merges","archive_url":"https://api.github.com/repos/JoshRosen/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/JoshRosen/spark/downloads","issues_url":"https://api.github.com/repos/JoshRosen/spark/issues{/number}","pulls_url":"https://api.github.com/repos/JoshRosen/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/JoshRosen/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/JoshRosen/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/JoshRosen/spark/labels{/name}","releases_url":"https://api.github.com/repos/JoshRosen/spark/releases{/id}","created_at":"2014-07-25T00:47:52Z","updated_at":"2015-05-01T22:45:32Z","pushed_at":"2015-05-06T22:22:10Z","git_url":"git://github.com/JoshRosen/spark.git","ssh_url":"git@github.com:JoshRosen/spark.git","clone_url":"https://github.com/JoshRosen/spark.git","svn_url":"https://github.com/JoshRosen/spark","homepage":null,"size":103979,"stargazers_count":1,"watchers_count":1,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"845d1d4d0cc5512d1671b2f2c7c74c5b97c3aef8","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":17165658,"name":"spark","full_name":"apache/spark","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/spark","description":"Mirror of Apache Spark","fork":false,"url":"https://api.github.com/repos/apache/spark","forks_url":"https://api.github.com/repos/apache/spark/forks","keys_url":"https://api.github.com/repos/apache/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/spark/teams","hooks_url":"https://api.github.com/repos/apache/spark/hooks","issue_events_url":"https://api.github.com/repos/apache/spark/issues/events{/number}","events_url":"https://api.github.com/repos/apache/spark/events","assignees_url":"https://api.github.com/repos/apache/spark/assignees{/user}","branches_url":"https://api.github.com/repos/apache/spark/branches{/branch}","tags_url":"https://api.github.com/repos/apache/spark/tags","blobs_url":"https://api.github.com/repos/apache/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/spark/languages","stargazers_url":"https://api.github.com/repos/apache/spark/stargazers","contributors_url":"https://api.github.com/repos/apache/spark/contributors","subscribers_url":"https://api.github.com/repos/apache/spark/subscribers","subscription_url":"https://api.github.com/repos/apache/spark/subscription","commits_url":"https://api.github.com/repos/apache/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/spark/contents/{+path}","compare_url":"https://api.github.com/repos/apache/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/spark/merges","archive_url":"https://api.github.com/repos/apache/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/spark/downloads","issues_url":"https://api.github.com/repos/apache/spark/issues{/number}","pulls_url":"https://api.github.com/repos/apache/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/spark/labels{/name}","releases_url":"https://api.github.com/repos/apache/spark/releases{/id}","created_at":"2014-02-25T08:00:08Z","updated_at":"2015-05-07T01:03:21Z","pushed_at":"2015-05-07T01:03:36Z","git_url":"git://github.com/apache/spark.git","ssh_url":"git@github.com:apache/spark.git","clone_url":"https://github.com/apache/spark.git","svn_url":"https://github.com/apache/spark","homepage":null,"size":1621667,"stargazers_count":3876,"watchers_count":3876,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3381,"mirror_url":"git://git.apache.org/spark.git","open_issues_count":311,"forks":3381,"open_issues":311,"watchers":3876,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/5868"},"html":{"href":"https://github.com/apache/spark/pull/5868"},"issue":{"href":"https://api.github.com/repos/apache/spark/issues/5868"},"comments":{"href":"https://api.github.com/repos/apache/spark/issues/5868/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/spark/pulls/5868/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/spark/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/spark/pulls/5868/commits"},"statuses":{"href":"https://api.github.com/repos/apache/spark/statuses/b67441239aa80acf78a863868c31405ed9bea8d6"}}}}