-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-49386][CORE][SQL] Add memory based thresholds for shuffle spill #47856
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
647afb8
044a232
27c6478
a3f5d16
14a7f2d
8374d18
57f0711
d734bfa
7f78264
b28bd7b
d083773
8225e94
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 |
|---|---|---|
|
|
@@ -58,6 +58,10 @@ private[spark] abstract class Spillable[C](taskMemoryManager: TaskMemoryManager) | |
| private[this] val numElementsForceSpillThreshold: Int = | ||
| SparkEnv.get.conf.get(SHUFFLE_SPILL_NUM_ELEMENTS_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| // Force this collection to spill when its size is greater than this threshold | ||
| private[this] val maxSizeForceSpillThreshold: Long = | ||
| SparkEnv.get.conf.get(SHUFFLE_SPILL_MAX_SIZE_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| // Threshold for this collection's size in bytes before we start tracking its memory usage | ||
| // To avoid a large number of small spills, initialize this to a value orders of magnitude > 0 | ||
| @volatile private[this] var myMemoryThreshold = initialMemoryThreshold | ||
|
|
@@ -80,21 +84,25 @@ private[spark] abstract class Spillable[C](taskMemoryManager: TaskMemoryManager) | |
| * @return true if `collection` was spilled to disk; false otherwise | ||
| */ | ||
| protected def maybeSpill(collection: C, currentMemory: Long): Boolean = { | ||
| var shouldSpill = false | ||
| if (elementsRead % 32 == 0 && currentMemory >= myMemoryThreshold) { | ||
| val shouldSpill = if (_elementsRead > numElementsForceSpillThreshold | ||
| || currentMemory > maxSizeForceSpillThreshold) { | ||
|
||
| // Check number of elements or memory usage limits, whichever is hit first | ||
| true | ||
| } else if (_elementsRead % 32 == 0 && currentMemory >= myMemoryThreshold) { | ||
| // Claim up to double our current memory from the shuffle memory pool | ||
| val amountToRequest = 2 * currentMemory - myMemoryThreshold | ||
| val granted = acquireMemory(amountToRequest) | ||
| myMemoryThreshold += granted | ||
| // If we were granted too little memory to grow further (either tryToAcquire returned 0, | ||
| // or we already had more memory than myMemoryThreshold), spill the current collection | ||
| shouldSpill = currentMemory >= myMemoryThreshold | ||
| currentMemory >= myMemoryThreshold | ||
| } else { | ||
| false | ||
| } | ||
| shouldSpill = shouldSpill || _elementsRead > numElementsForceSpillThreshold | ||
attilapiros marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // Actually spill | ||
| if (shouldSpill) { | ||
| _spillCount += 1 | ||
| logSpillage(currentMemory) | ||
| logSpillage(currentMemory, _elementsRead) | ||
| spill(collection) | ||
| _elementsRead = 0 | ||
| _memoryBytesSpilled += currentMemory | ||
|
|
@@ -140,12 +148,14 @@ private[spark] abstract class Spillable[C](taskMemoryManager: TaskMemoryManager) | |
| * Prints a standard log message detailing spillage. | ||
| * | ||
| * @param size number of bytes spilled | ||
| * @param elements number of elements read from input since last spill | ||
| */ | ||
| @inline private def logSpillage(size: Long): Unit = { | ||
| @inline private def logSpillage(size: Long, elements: Int): Unit = { | ||
| val threadId = Thread.currentThread().getId | ||
| logInfo(log"Thread ${MDC(LogKeys.THREAD_ID, threadId)} " + | ||
| log"spilling in-memory map of ${MDC(LogKeys.BYTE_SIZE, | ||
| org.apache.spark.util.Utils.bytesToString(size))} to disk " + | ||
| org.apache.spark.util.Utils.bytesToString(size))} " + | ||
| log"(elements: ${MDC(LogKeys.NUM_ELEMENTS_SPILL_RECORDS, elements)}) to disk " + | ||
| log"(${MDC(LogKeys.NUM_SPILLS, _spillCount)} times so far)") | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3356,6 +3356,13 @@ object SQLConf { | |
| .intConf | ||
| .createWithDefault(SHUFFLE_SPILL_NUM_ELEMENTS_FORCE_SPILL_THRESHOLD.defaultValue.get) | ||
|
|
||
| val WINDOW_EXEC_BUFFER_SIZE_SPILL_THRESHOLD = | ||
| buildConf("spark.sql.windowExec.buffer.spill.size.threshold") | ||
|
||
| .internal() | ||
| .doc("Threshold for size of rows to be spilled by window operator") | ||
| .version("4.1.0") | ||
| .fallbackConf(SHUFFLE_SPILL_MAX_SIZE_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| val WINDOW_GROUP_LIMIT_THRESHOLD = | ||
| buildConf("spark.sql.optimizer.windowGroupLimitThreshold") | ||
| .internal() | ||
|
|
@@ -3377,6 +3384,15 @@ object SQLConf { | |
| .intConf | ||
| .createWithDefault(4096) | ||
|
|
||
| val SESSION_WINDOW_BUFFER_SPILL_SIZE_THRESHOLD = | ||
| buildConf("spark.sql.sessionWindow.buffer.spill.size.threshold") | ||
| .internal() | ||
| .doc("Threshold for size of rows to be spilled by window operator. Note that " + | ||
| "the buffer is used only for the query Spark cannot apply aggregations on determining " + | ||
| "session window.") | ||
| .version("4.1.0") | ||
| .fallbackConf(SHUFFLE_SPILL_MAX_SIZE_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| val SESSION_WINDOW_BUFFER_SPILL_THRESHOLD = | ||
| buildConf("spark.sql.sessionWindow.buffer.spill.threshold") | ||
| .internal() | ||
|
|
@@ -3420,6 +3436,13 @@ object SQLConf { | |
| .intConf | ||
| .createWithDefault(SHUFFLE_SPILL_NUM_ELEMENTS_FORCE_SPILL_THRESHOLD.defaultValue.get) | ||
|
|
||
| val SORT_MERGE_JOIN_EXEC_BUFFER_SIZE_SPILL_THRESHOLD = | ||
| buildConf("spark.sql.sortMergeJoinExec.buffer.spill.size.threshold") | ||
| .internal() | ||
| .doc("Threshold for size of rows to be spilled by sort merge join operator") | ||
| .version("4.1.0") | ||
| .fallbackConf(SHUFFLE_SPILL_MAX_SIZE_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| val CARTESIAN_PRODUCT_EXEC_BUFFER_IN_MEMORY_THRESHOLD = | ||
| buildConf("spark.sql.cartesianProductExec.buffer.in.memory.threshold") | ||
| .internal() | ||
|
|
@@ -3437,6 +3460,13 @@ object SQLConf { | |
| .intConf | ||
| .createWithDefault(SHUFFLE_SPILL_NUM_ELEMENTS_FORCE_SPILL_THRESHOLD.defaultValue.get) | ||
|
|
||
| val CARTESIAN_PRODUCT_EXEC_BUFFER_SIZE_SPILL_THRESHOLD = | ||
| buildConf("spark.sql.cartesianProductExec.buffer.spill.size.threshold") | ||
| .internal() | ||
| .doc("Threshold for size of rows to be spilled by cartesian product operator") | ||
| .version("4.1.0") | ||
| .fallbackConf(SHUFFLE_SPILL_MAX_SIZE_FORCE_SPILL_THRESHOLD) | ||
|
|
||
| val SUPPORT_QUOTED_REGEX_COLUMN_NAME = buildConf("spark.sql.parser.quotedRegexColumnNames") | ||
| .doc("When true, quoted Identifiers (using backticks) in SELECT statement are interpreted" + | ||
| " as regular expressions.") | ||
|
|
@@ -6679,24 +6709,35 @@ class SQLConf extends Serializable with Logging with SqlApiConf { | |
|
|
||
| def windowExecBufferSpillThreshold: Int = getConf(WINDOW_EXEC_BUFFER_SPILL_THRESHOLD) | ||
|
|
||
| def windowExecBufferSpillSizeThreshold: Long = getConf(WINDOW_EXEC_BUFFER_SIZE_SPILL_THRESHOLD) | ||
|
|
||
| def windowGroupLimitThreshold: Int = getConf(WINDOW_GROUP_LIMIT_THRESHOLD) | ||
|
|
||
| def sessionWindowBufferInMemoryThreshold: Int = getConf(SESSION_WINDOW_BUFFER_IN_MEMORY_THRESHOLD) | ||
|
|
||
| def sessionWindowBufferSpillThreshold: Int = getConf(SESSION_WINDOW_BUFFER_SPILL_THRESHOLD) | ||
|
|
||
| def sessionWindowBufferSpillSizeThreshold: Long = | ||
| getConf(SESSION_WINDOW_BUFFER_SPILL_SIZE_THRESHOLD) | ||
|
|
||
| def sortMergeJoinExecBufferInMemoryThreshold: Int = | ||
| getConf(SORT_MERGE_JOIN_EXEC_BUFFER_IN_MEMORY_THRESHOLD) | ||
|
|
||
| def sortMergeJoinExecBufferSpillThreshold: Int = | ||
| getConf(SORT_MERGE_JOIN_EXEC_BUFFER_SPILL_THRESHOLD) | ||
|
|
||
| def sortMergeJoinExecBufferSpillSizeThreshold: Long = | ||
| getConf(SORT_MERGE_JOIN_EXEC_BUFFER_SIZE_SPILL_THRESHOLD) | ||
|
|
||
| def cartesianProductExecBufferInMemoryThreshold: Int = | ||
| getConf(CARTESIAN_PRODUCT_EXEC_BUFFER_IN_MEMORY_THRESHOLD) | ||
|
|
||
| def cartesianProductExecBufferSpillThreshold: Int = | ||
| getConf(CARTESIAN_PRODUCT_EXEC_BUFFER_SPILL_THRESHOLD) | ||
|
|
||
| def cartesianProductExecBufferSizeSpillThreshold: Long = | ||
| getConf(CARTESIAN_PRODUCT_EXEC_BUFFER_SIZE_SPILL_THRESHOLD) | ||
|
|
||
| def codegenSplitAggregateFunc: Boolean = getConf(SQLConf.CODEGEN_SPLIT_AGGREGATE_FUNC) | ||
|
|
||
| def maxNestedViewDepth: Int = getConf(SQLConf.MAX_NESTED_VIEW_DEPTH) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.