Skip to content

Commit 62f3154

Browse files
committed
[query] use a single regionpool per thread
1 parent 48a9b93 commit 62f3154

46 files changed

Lines changed: 1565 additions & 1632 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

hail/hail/src/is/hail/annotations/RegionPool.scala

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,7 @@ final class RegionPool private (strictMemoryCheck: Boolean, threadName: String,
118118
}
119119
}
120120

121-
def getRegion(): Region = getRegion(Region.REGULAR)
122-
123-
def getRegion(size: Int): Region = {
121+
def getRegion(size: Int = Region.REGULAR): Region = {
124122
val r = new Region(size, this)
125123
r.memory = getMemory(size)
126124
r
@@ -154,11 +152,29 @@ final class RegionPool private (strictMemoryCheck: Boolean, threadName: String,
154152

155153
def report(context: String): Unit = {
156154
val inBlocks = bytesInBlocks()
155+
val (chunksAllocated, cacheHits) = chunkCache.getUsage()
157156

158157
logger.info(
159-
s"RegionPool: $context: ${readableBytes(totalAllocatedBytes)} allocated (${readableBytes(inBlocks)} blocks / " +
160-
s"${readableBytes(totalAllocatedBytes - inBlocks)} chunks), regions.size = ${regions.size}, " +
161-
s"$numJavaObjects current java objects, thread $threadID: $threadName"
158+
s"""RegionPool: $context
159+
| thread:
160+
| id: $threadID
161+
| name: $threadName
162+
| objects: $numJavaObjects
163+
| allocations:
164+
| peak: $getHighestTotalUsage
165+
| total: ${readableBytes(totalAllocatedBytes)}
166+
| blocks: ${readableBytes(inBlocks)}
167+
| chunks: ${readableBytes(totalAllocatedBytes - inBlocks)}
168+
| regions:
169+
| total: ${regions.size}
170+
| free: ${freeRegions.size}
171+
| blocks:
172+
| total: ${blocks.sum}
173+
| free: ${freeBlocks.view.map(_.size).sum}
174+
| chunks:
175+
| total: $chunksAllocated
176+
| reused: $cacheHits
177+
| """.stripMargin
162178
)
163179
// logger.info("-----------STACK_TRACES---------")
164180
// val stacks: String = regions.result().toIndexedSeq.flatMap(r => r.stackTrace.map((r.getTotalChunkMemory(), _))).foldLeft("")((a: String, b) => a + "\n" + b.toString())
@@ -170,8 +186,6 @@ final class RegionPool private (strictMemoryCheck: Boolean, threadName: String,
170186
def scopedSmallRegion[T](f: Region => T): T = using(Region(Region.SMALL, pool = this))(f)
171187
def scopedTinyRegion[T](f: Region => T): T = using(Region(Region.TINY, pool = this))(f)
172188

173-
override def finalize(): Unit = close()
174-
175189
private[this] var closed: Boolean = false
176190

177191
override def close(): Unit = {

hail/hail/src/is/hail/backend/ExecuteContext.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package is.hail.backend
33
import is.hail.HailFeatureFlags
44
import is.hail.annotations.{Region, RegionPool}
55
import is.hail.asm4s.HailClassLoader
6-
import is.hail.backend.local.LocalTaskContext
76
import is.hail.expr.ir.{BaseIR, CompileCache, Compiled}
87
import is.hail.expr.ir.LoweredTableReader.LoweredTableReaderCoercer
98
import is.hail.expr.ir.lowering.IrMetadata
@@ -72,7 +71,7 @@ object ExecuteContext {
7271
coercerCache: mutable.Map[Any, LoweredTableReaderCoercer],
7372
)(
7473
f: ExecuteContext => T
75-
): T = {
74+
): T =
7675
RegionPool.scoped { pool =>
7776
pool.scopedRegion { region =>
7877
using(new ExecuteContext(
@@ -94,7 +93,6 @@ object ExecuteContext {
9493
))(f(_))
9594
}
9695
}
97-
}
9896

9997
def createTmpPathNoCleanup(tmpdir: String, prefix: String, extension: String = null): String = {
10098
val random = new SecureRandom()
@@ -113,7 +111,7 @@ class ExecuteContext(
113111
val backend: Backend,
114112
val references: Map[String, ReferenceGenome],
115113
val fs: FS,
116-
val r: Region,
114+
override val r: Region,
117115
val timer: ExecutionTimer,
118116
val tempFileManager: TempFileManager,
119117
val theHailClassLoader: HailClassLoader,
@@ -123,7 +121,7 @@ class ExecuteContext(
123121
val CompileCache: CompileCache,
124122
val PersistedIrCache: mutable.Map[Int, BaseIR],
125123
val PersistedCoercerCache: mutable.Map[Any, LoweredTableReaderCoercer],
126-
) extends Closeable {
124+
) extends HailTaskContext with Closeable {
127125

128126
val rngNonce: Long =
129127
try
@@ -142,10 +140,14 @@ class ExecuteContext(
142140

143141
val memo: mutable.Map[Any, Any] = new mutable.HashMap[Any, Any]()
144142

145-
val taskContext: HailTaskContext = new LocalTaskContext(0, 0)
143+
private[this] val onCloseTasks = mutable.ArrayBuffer.empty[() => Unit]
144+
override def onClose(f: () => Unit): Unit = onCloseTasks += f
145+
146+
def run[A](f: Compiled[A])(implicit E: Enclosing): A =
147+
time(f(theHailClassLoader, fs, this, r))
146148

147149
def scopedExecution[T](f: Compiled[T])(implicit E: Enclosing): T =
148-
using(new LocalTaskContext(0, 0))(tc => time(f(theHailClassLoader, fs, tc, r)))
150+
r.pool.scopedRegion(r => local(r = r)(_.run(f)))
149151

150152
def createTmpPath(prefix: String, extension: String = null, local: Boolean = false): String =
151153
tempFileManager.newTmpPath(if (local) localTmpdir else tmpdir, prefix, extension)
@@ -159,8 +161,8 @@ class ExecuteContext(
159161
def shouldLogIR(): Boolean = !shouldNotLogIR()
160162

161163
override def close(): Unit = {
164+
onCloseTasks.foreach(_())
162165
tempFileManager.close()
163-
taskContext.close()
164166
}
165167

166168
def time[A](block: => A)(implicit E: Enclosing): A =
@@ -179,7 +181,7 @@ class ExecuteContext(
179181
flags: HailFeatureFlags = this.flags,
180182
irMetadata: IrMetadata = this.irMetadata,
181183
blockMatrixCache: mutable.Map[String, BlockMatrix] = this.BlockMatrixCache,
182-
codeCache: CompileCache = this.CompileCache,
184+
compileCache: CompileCache = this.CompileCache,
183185
persistedIrCache: mutable.Map[Int, BaseIR] = this.PersistedIrCache,
184186
persistedCoercerCache: mutable.Map[Any, LoweredTableReaderCoercer] = this.PersistedCoercerCache,
185187
)(
@@ -198,7 +200,7 @@ class ExecuteContext(
198200
flags,
199201
irMetadata,
200202
blockMatrixCache,
201-
codeCache,
203+
compileCache,
202204
persistedIrCache,
203205
persistedCoercerCache,
204206
))(f)
Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,35 @@
11
package is.hail.backend
22

3-
import is.hail.annotations.RegionPool
4-
import is.hail.utils._
3+
import is.hail.annotations.{Region, RegionPool}
4+
import is.hail.utils.using
55

66
import scala.collection.mutable
77

8-
import java.io.Closeable
8+
trait HailTaskContext {
99

10-
class TaskFinalizer {
11-
val closeables = mutable.ArrayBuffer.empty[Closeable]
10+
/** region whose lifetime is at least as long as this task */
11+
def r: Region
1212

13-
def clear(): Unit =
14-
closeables.clear()
15-
16-
def addCloseable(c: Closeable): Unit =
17-
closeables += c
18-
19-
def closeAll(): Unit = closeables.foreach(_.close())
13+
/** register an action that will be called when this task completes */
14+
def onClose(f: () => Unit): Unit
2015
}
2116

22-
abstract class HailTaskContext extends AutoCloseable with Logging {
23-
def stageId(): Int
24-
25-
def partitionId(): Int
26-
27-
def attemptNumber(): Int
28-
29-
private lazy val thePool = RegionPool()
30-
31-
def getRegionPool(): RegionPool = thePool
17+
object HailTaskContext {
18+
def runPartition[A](partId: Int)(f: HailTaskContext => A): A =
19+
using(new PartitionContext(partId))(f)
20+
}
3221

33-
val finalizers = mutable.ArrayBuffer.empty[TaskFinalizer]
22+
class PartitionContext(partId: Int) extends HailTaskContext with AutoCloseable {
23+
private[this] val onCloseTasks = mutable.ArrayBuffer.empty[() => Unit]
3424

35-
def newFinalizer(): TaskFinalizer = {
36-
val f = new TaskFinalizer
37-
finalizers += f
38-
f
39-
}
25+
private[this] val pool = RegionPool()
26+
override val r: Region = Region(pool = pool)
27+
override def onClose(f: () => Unit): Unit = onCloseTasks += f
4028

4129
override def close(): Unit = {
42-
logger.info(
43-
s"TaskReport: stage=${stageId()}, partition=${partitionId()}, attempt=${attemptNumber()}, " +
44-
s"peakBytes=${thePool.getHighestTotalUsage}, peakBytesReadable=${formatSpace(thePool.getHighestTotalUsage)}, " +
45-
s"chunks requested=${thePool.getUsage._1}, cache hits=${thePool.getUsage._2}"
46-
)
47-
finalizers.foreach(_.closeAll())
48-
thePool.close()
30+
onCloseTasks.foreach(_())
31+
r.close()
32+
pool.logStats(s"Partition $partId")
33+
pool.close()
4934
}
5035
}

hail/hail/src/is/hail/backend/local/LocalBackend.scala

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ import com.fasterxml.jackson.core.StreamReadConstraints
2020

2121
class LocalBroadcastValue[T](val value: T) extends BroadcastValue[T] with Serializable
2222

23-
class LocalTaskContext(val partitionId: Int, val stageId: Int) extends HailTaskContext {
24-
override def attemptNumber(): Int = 0
25-
}
26-
2723
object LocalBackend extends Backend with Logging {
2824

2925
// From https://github.com/hail-is/hail/issues/14580 :
@@ -43,16 +39,7 @@ object LocalBackend extends Backend with Logging {
4339
override def broadcast[T: ClassTag](value: T): BroadcastValue[T] =
4440
new LocalBroadcastValue[T](value)
4541

46-
private[this] var stageIdx: Int = 0
47-
48-
private[this] def nextStageId(): Int =
49-
synchronized {
50-
val current = stageIdx
51-
stageIdx += 1
52-
current
53-
}
54-
55-
override def runtimeContext(ctx: ExecuteContext): DriverRuntimeContext = {
42+
override def runtimeContext(ctx: ExecuteContext): DriverRuntimeContext =
5643
new DriverRuntimeContext {
5744

5845
override val executionCache: ExecutionCache =
@@ -77,14 +64,10 @@ object LocalBackend extends Backend with Logging {
7764
var failure: Option[Throwable] =
7865
None
7966

80-
val stageId = nextStageId()
81-
8267
try
8368
for (idx <- todo)
84-
results += using(new LocalTaskContext(idx, stageId)) { htc =>
85-
htc.getRegionPool().scopedRegion { r =>
86-
f(ctx.theHailClassLoader, ctx.fs, htc, r)(globals, contexts(idx)) -> idx
87-
}
69+
results += ctx.scopedExecution { (hcl, fs, ctx, r) =>
70+
(f(hcl, fs, ctx, r)(globals, contexts(idx)), idx)
8871
}
8972
catch {
9073
case NonFatal(t) =>
@@ -94,12 +77,10 @@ object LocalBackend extends Backend with Logging {
9477
(failure, results.result())
9578
}
9679
}
97-
}
9880

9981
override def defaultParallelism: Int = 1
10082

101-
override def close(): Unit =
102-
synchronized { stageIdx = 0 }
83+
override def close(): Unit = {}
10384

10485
private[this] def _jvmLowerAndExecute(
10586
ctx: ExecuteContext,

hail/hail/src/is/hail/backend/service/ServiceBackend.scala

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import is.hail.Revision
44
import is.hail.backend._
55
import is.hail.backend.Backend.PartitionFn
66
import is.hail.backend.ExecutionCache.Flags.UseFastRestarts
7-
import is.hail.backend.local.LocalTaskContext
87
import is.hail.backend.service.ServiceBackend.MaxConcurrentPartitionReads
98
import is.hail.collection.FastSeq
109
import is.hail.collection.compat.immutable.ArraySeq
@@ -357,10 +356,8 @@ class ServiceBackend(
357356
partitions.getOrElse(contexts.indices) match {
358357
case Seq(k) =>
359358
try
360-
using(new LocalTaskContext(k, stageCount)) { htc =>
361-
None -> htc.getRegionPool().scopedRegion { r =>
362-
FastSeq(f(ctx.theHailClassLoader, ctx.fs, htc, r)(globals, contexts(k)) -> k)
363-
}
359+
ctx.scopedExecution { (hcl, fs, htc, r) =>
360+
(None, FastSeq(f(hcl, fs, htc, r)(globals, contexts(k)) -> k))
364361
}
365362
catch {
366363
case NonFatal(t) => Some(t) -> ArraySeq.empty

hail/hail/src/is/hail/backend/service/Worker.scala

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ import java.util
1919
import java.util.concurrent.Executors
2020
import java.util.concurrent.atomic.AtomicInteger
2121

22-
class ServiceTaskContext(val partitionId: Int) extends HailTaskContext {
23-
override def stageId(): Int = 0
24-
25-
override def attemptNumber(): Int = 0
26-
}
27-
2822
class WorkerTimer extends Logging {
2923

3024
var startTimes: mutable.Map[String, Long] = mutable.Map()
@@ -250,12 +244,11 @@ object Worker extends Logging {
250244
inputs.flatMap { case (globals, context, f) =>
251245
timer.enter("execute") {
252246
try
253-
using(new ServiceTaskContext(partition)) { htc =>
254-
retryTransientErrors {
255-
htc.getRegionPool().scopedRegion { r =>
256-
Right(f(hcl, fs, htc, r)(globals, context))
257-
}
258-
}
247+
HailTaskContext.runPartition(partition) { htc =>
248+
retryTransientErrors(
249+
Right(f(hcl, fs, htc, htc.r)(globals, context)),
250+
Some(() => htc.r.clear()),
251+
)
259252
}
260253
catch {
261254
case t: Throwable => Left(t)

hail/hail/src/is/hail/backend/spark/SparkBackend.scala

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package is.hail.backend.spark
22

33
import is.hail.annotations._
44
import is.hail.asm4s._
5-
import is.hail.backend._
5+
import is.hail.backend.{PartitionContext, _}
66
import is.hail.backend.Backend.PartitionFn
77
import is.hail.collection.compat.immutable.ArraySeq
88
import is.hail.expr.Validate
@@ -36,31 +36,23 @@ class SparkBroadcastValue[T](bc: Broadcast[T]) extends BroadcastValue[T] with Se
3636
}
3737

3838
object SparkTaskContext {
39-
def get(): SparkTaskContext = taskContext.get
39+
def get: HailTaskContext = taskContext.get
4040

41-
private[this] val taskContext: ThreadLocal[SparkTaskContext] =
42-
new ThreadLocal[SparkTaskContext]() {
43-
override def initialValue(): SparkTaskContext = {
41+
private[this] val taskContext: ThreadLocal[HailTaskContext] =
42+
new ThreadLocal[HailTaskContext]() {
43+
override def initialValue(): HailTaskContext = {
4444
val sparkTC = TaskContext.get()
4545
assert(sparkTC != null, "Spark Task Context was null, maybe this ran on the driver?")
46-
sparkTC.addTaskCompletionListener[Unit]((_: TaskContext) => SparkTaskContext.finish()): Unit
4746

48-
// this must be the only place where SparkTaskContext classes are created
49-
new SparkTaskContext(sparkTC)
47+
val htc = new PartitionContext(sparkTC.stageId())
48+
sparkTC.addTaskCompletionListener[Unit] { _ => htc.close(); remove(); }: Unit
49+
50+
htc
5051
}
5152
}
5253

53-
def finish(): Unit = {
54-
taskContext.get().close()
54+
def finish(): Unit =
5555
taskContext.remove()
56-
}
57-
}
58-
59-
class SparkTaskContext private[spark] (ctx: TaskContext) extends HailTaskContext {
60-
self =>
61-
override def stageId(): Int = ctx.stageId()
62-
override def partitionId(): Int = ctx.partitionId()
63-
override def attemptNumber(): Int = ctx.attemptNumber()
6456
}
6557

6658
object SparkBackend extends Logging {
@@ -268,11 +260,9 @@ class SparkBackend(val spark: SparkSession) extends Backend with Logging {
268260

269261
override def compute(partition: Partition, context: TaskContext)
270262
: Iterator[Array[Byte]] = {
271-
val htc = SparkTaskContext.get()
272-
htc.getRegionPool().scopedRegion { r =>
273-
val g = f(theHailClassLoaderForSparkWorkers, new HadoopFS(fsConfig), htc, r)
274-
Iterator.single(g(globals, partition.asInstanceOf[RDDPartition].data))
275-
}
263+
val ctx = SparkTaskContext.get
264+
val g = f(theHailClassLoaderForSparkWorkers, new HadoopFS(fsConfig), ctx, ctx.r)
265+
Iterator.single(g(globals, partition.asInstanceOf[RDDPartition].data))
276266
}
277267
}
278268

0 commit comments

Comments
 (0)