-
Notifications
You must be signed in to change notification settings - Fork 28.9k
[SPARK-8029][core] shuffleoutput per attempt #6648
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
Changes from all commits
d08c20c
89e8428
70a787b
7fbcefb
2eebbf2
7142242
ccaa159
3585b96
de23530
c91ee10
05c72fd
5dc5436
37eece8
31c21fa
a894be1
93592b1
ea2d972
de0a596
6654c53
dd2839d
e684928
2523431
4d976f4
2b723fd
fd40a93
b5d8ec5
9f01d7e
fae9c0c
06daceb
cd16ee8
2006de8
e905f6d
86e651c
b16e7f2
fdcc92d
66d5bf5
289576d
9a06fe2
9befe51
87d7ddd
1072a44
89a93ae
ece31ba
de62da0
a7f2d9a
4bfbf94
f9a1a31
9bdfdc1
b762e22
8bbda62
ff1870a
e2daa05
54948a8
c231221
52eba21
64ead29
3b4159b
7284589
fd81700
659cb45
f1d5c1c
2720425
bcdbf54
b996802
c29fa57
d56f8d8
55a9bb1
7b465a7
9d1189f
c297c78
e3c8df6
78d9614
529aa95
23af915
c288ff9
657b135
f392acc
9cd9c75
90ee54a
26b6ea6
c60c6d4
5547611
c7b3017
812aa0e
26baad9
f37be91
fac0f1c
a38d760
37ac799
c9a9e08
fbd129b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,9 @@ private[spark] sealed trait MapStatus { | |
| /** Location where this task was run. */ | ||
| def location: BlockManagerId | ||
|
|
||
| /** stage attempt for the ShuffleMapTask */ | ||
| def stageAttemptId: Int | ||
|
|
||
| /** | ||
| * Estimated size for the reduce block, in bytes. | ||
| * | ||
|
|
@@ -43,11 +46,11 @@ private[spark] sealed trait MapStatus { | |
|
|
||
| private[spark] object MapStatus { | ||
|
|
||
| def apply(loc: BlockManagerId, uncompressedSizes: Array[Long]): MapStatus = { | ||
| def apply(loc: BlockManagerId, stageAttemptId: Int, uncompressedSizes: Array[Long]): MapStatus = { | ||
| if (uncompressedSizes.length > 2000) { | ||
| HighlyCompressedMapStatus(loc, uncompressedSizes) | ||
| HighlyCompressedMapStatus(loc, stageAttemptId, uncompressedSizes) | ||
| } else { | ||
| new CompressedMapStatus(loc, uncompressedSizes) | ||
| new CompressedMapStatus(loc, stageAttemptId, uncompressedSizes) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -90,29 +93,34 @@ private[spark] object MapStatus { | |
| */ | ||
| private[spark] class CompressedMapStatus( | ||
| private[this] var loc: BlockManagerId, | ||
| private[this] var _stageAttemptId: Int, | ||
| private[this] var compressedSizes: Array[Byte]) | ||
| extends MapStatus with Externalizable { | ||
|
|
||
| protected def this() = this(null, null.asInstanceOf[Array[Byte]]) // For deserialization only | ||
| protected def this() = this(null, 0, null.asInstanceOf[Array[Byte]]) // For deserialization only | ||
|
|
||
| def this(loc: BlockManagerId, uncompressedSizes: Array[Long]) { | ||
| this(loc, uncompressedSizes.map(MapStatus.compressSize)) | ||
| def this(loc: BlockManagerId, stageAttemptId: Int, uncompressedSizes: Array[Long]) { | ||
| this(loc, stageAttemptId, uncompressedSizes.map(MapStatus.compressSize)) | ||
| } | ||
|
|
||
| override def location: BlockManagerId = loc | ||
|
|
||
| override def stageAttemptId: Int = _stageAttemptId | ||
|
|
||
| override def getSizeForBlock(reduceId: Int): Long = { | ||
| MapStatus.decompressSize(compressedSizes(reduceId)) | ||
| } | ||
|
|
||
| override def writeExternal(out: ObjectOutput): Unit = Utils.tryOrIOException { | ||
| loc.writeExternal(out) | ||
| out.writeInt(_stageAttemptId) | ||
| out.writeInt(compressedSizes.length) | ||
| out.write(compressedSizes) | ||
| } | ||
|
|
||
| override def readExternal(in: ObjectInput): Unit = Utils.tryOrIOException { | ||
| loc = BlockManagerId(in) | ||
| _stageAttemptId = in.readInt() | ||
| val len = in.readInt() | ||
| compressedSizes = new Array[Byte](len) | ||
| in.readFully(compressedSizes) | ||
|
|
@@ -131,6 +139,7 @@ private[spark] class CompressedMapStatus( | |
| */ | ||
| private[spark] class HighlyCompressedMapStatus private ( | ||
| private[this] var loc: BlockManagerId, | ||
| private[this] var _stageAttemptId: Int, | ||
| private[this] var numNonEmptyBlocks: Int, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your code, but it doesn't look like there is any reason for this to be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is required for the deserialization code in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh sorry, I wasn't paying close enough attention, you are right |
||
| private[this] var emptyBlocks: BitSet, | ||
| private[this] var avgSize: Long) | ||
|
|
@@ -140,10 +149,12 @@ private[spark] class HighlyCompressedMapStatus private ( | |
| require(loc == null || avgSize > 0 || numNonEmptyBlocks == 0, | ||
| "Average size can only be zero for map stages that produced no output") | ||
|
|
||
| protected def this() = this(null, -1, null, -1) // For deserialization only | ||
| protected def this() = this(null, 0, -1, null, -1) // For deserialization only | ||
|
|
||
| override def location: BlockManagerId = loc | ||
|
|
||
| override def stageAttemptId: Int = _stageAttemptId | ||
|
|
||
| override def getSizeForBlock(reduceId: Int): Long = { | ||
| if (emptyBlocks.get(reduceId)) { | ||
| 0 | ||
|
|
@@ -154,20 +165,25 @@ private[spark] class HighlyCompressedMapStatus private ( | |
|
|
||
| override def writeExternal(out: ObjectOutput): Unit = Utils.tryOrIOException { | ||
| loc.writeExternal(out) | ||
| out.writeInt(_stageAttemptId) | ||
| emptyBlocks.writeExternal(out) | ||
| out.writeLong(avgSize) | ||
| } | ||
|
|
||
| override def readExternal(in: ObjectInput): Unit = Utils.tryOrIOException { | ||
| loc = BlockManagerId(in) | ||
| _stageAttemptId = in.readInt() | ||
| emptyBlocks = new BitSet | ||
| emptyBlocks.readExternal(in) | ||
| avgSize = in.readLong() | ||
| } | ||
| } | ||
|
|
||
| private[spark] object HighlyCompressedMapStatus { | ||
| def apply(loc: BlockManagerId, uncompressedSizes: Array[Long]): HighlyCompressedMapStatus = { | ||
| def apply( | ||
| loc: BlockManagerId, | ||
| stageAttemptId: Int, | ||
| uncompressedSizes: Array[Long]): HighlyCompressedMapStatus = { | ||
| // We must keep track of which blocks are empty so that we don't report a zero-sized | ||
| // block as being non-empty (or vice-versa) when using the average block size. | ||
| var i = 0 | ||
|
|
@@ -193,6 +209,6 @@ private[spark] object HighlyCompressedMapStatus { | |
| } else { | ||
| 0 | ||
| } | ||
| new HighlyCompressedMapStatus(loc, numNonEmptyBlocks, emptyBlocks, avgSize) | ||
| new HighlyCompressedMapStatus(loc, stageAttemptId, numNonEmptyBlocks, emptyBlocks, avgSize) | ||
| } | ||
| } | ||
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.
MapStatusnow needs to keep astageAttemptIdso the fetch side knows which stage attempt to read data from. This is a negligible increase to the size of the map status when we have just one stage attempt, since its just an extra int. But when there are multiple attempts, we now have oneMapStatusper (location, stageAttempt). In pathalogical cases, this will result in many moreMapStatus.I needed to update the serialization of
MapStatus, but I believe this should be covered by existing tests.