Skip to content

Commit 6cd2d38

Browse files
committed
Optimization: resizable workers array
Instead of allocating an array of maxPoolSize (~2M) elements for the worst-case supported scenario that may never be reached in practice and takes considerable memory, allocate just an array of corePoolSize elements and grow it dynamically if needed to accommodate more workers. The data-structure to make it happen must support lock-free reads for performance reasons, but it is simple, since workers array is modified exclusively under synchronization.
1 parent 881cf68 commit 6cd2d38

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.coroutines.internal
6+
7+
import kotlinx.coroutines.*
8+
import java.util.concurrent.atomic.*
9+
10+
/**
11+
* Atomic array with lock-free reads and synchronized modifications. It is logically has unbounded size,
12+
* is implicitly filled with nulls, and is resized on updates as needed to grow.
13+
*/
14+
internal class ResizableAtomicArray<T>(initialLength: Int) {
15+
@Volatile
16+
private var array = AtomicReferenceArray<T>(initialLength)
17+
18+
public fun length(): Int = array.length()
19+
20+
public operator fun get(index: Int): T? {
21+
assert { index >= 0 }
22+
val array = this.array // volatile read
23+
return if (index < array.length()) array[index] else null
24+
}
25+
26+
@Synchronized
27+
operator fun set(index: Int, value: T?) {
28+
assert { index >= 0 }
29+
val curArray = this.array
30+
val curLen = curArray.length()
31+
if (index < curLen) {
32+
curArray[index] = value
33+
} else {
34+
val newArray = AtomicReferenceArray<T>((index + 1).coerceAtLeast(2 * curLen))
35+
for (i in 0 until curLen) newArray[i] = curArray[i]
36+
newArray[index] = value
37+
array = newArray // copy done
38+
}
39+
}
40+
}

kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
99
import kotlinx.coroutines.internal.*
1010
import java.io.*
1111
import java.util.concurrent.*
12-
import java.util.concurrent.atomic.*
1312
import java.util.concurrent.locks.*
1413
import kotlin.math.*
1514
import kotlin.random.*
@@ -261,7 +260,7 @@ internal class CoroutineScheduler(
261260
* works properly
262261
*/
263262
@JvmField
264-
val workers = AtomicReferenceArray<Worker?>(maxPoolSize + 1)
263+
val workers = ResizableAtomicArray<Worker?>(corePoolSize + 1)
265264

266265
/**
267266
* Long describing state of workers in this pool.

0 commit comments

Comments
 (0)