Use common ForkJoinPool for light calculations

This commit is contained in:
DBotThePony 2023-10-03 14:34:20 +07:00
parent 425494e104
commit f874b880ef
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 20 additions and 73 deletions

View File

@ -75,7 +75,7 @@ fun main() {
for (chunkX in 0 .. 100) {
//for (chunkX in 0 .. 17) {
// for (chunkY in 21 .. 21) {
for (chunkY in 0 .. 24) {
for (chunkY in 18 .. 24) {
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
val data2 = db.read(byteArrayOf(2, 0, chunkX.toByte(), 0, chunkY.toByte()))

View File

@ -88,7 +88,7 @@ class StarboundClient : Closeable {
val thread: Thread = Thread.currentThread()
// client specific executor which will accept tasks which involve probable
// callback to foreground executor to initialize thread-unsafe data
// In above case multiple threads will introduce big congestion for resources
// In above case too many threads will introduce big congestion for resources, stalling entire workload; wasting cpu resources
val backgroundExecutor = ForkJoinPool(Runtime.getRuntime().availableProcessors().coerceAtMost(4))
val foregroundExecutor = ManualExecutorService(thread)
val capabilities: GLCapabilities

View File

@ -8,8 +8,13 @@ import ru.dbotthepony.kvector.arrays.Object2DArray
import ru.dbotthepony.kvector.util.linearInterpolation
import ru.dbotthepony.kvector.vector.RGBAColor
import java.nio.ByteBuffer
import java.util.concurrent.Callable
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.Future
import java.util.concurrent.locks.LockSupport
import java.util.function.Supplier
import kotlin.math.roundToInt
import kotlin.random.Random
@ -380,10 +385,9 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
// perform multithreaded calculation only when it makes sense
if (multithreaded && pointLights.size > 1) {
val thread = Thread.currentThread()
// calculate k-means clusters of point lights
// to effectively utilize CPU cores
val clusterCount = threads.size.coerceAtMost(pointLights.size)
val clusterCount = ForkJoinPool.commonPool().parallelism.coerceAtMost(pointLights.size)
val clusters = ArrayList<TaskCluster>(clusterCount)
val startingPoints = IntArraySet()
// val rand = Random(System.nanoTime())
@ -394,7 +398,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
}
for (index in startingPoints.intIterator()) {
clusters.add(TaskCluster(pointLights[index].x.toDouble(), pointLights[index].y.toDouble(), this, thread))
clusters.add(TaskCluster(pointLights[index].x.toDouble(), pointLights[index].y.toDouble(), this))
}
var converged = false
@ -459,25 +463,14 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
}
}
/*for ((i, cluster) in clusters.withIndex()) {
val (r, g, b) = clusterColors[i]
val tasks = ArrayList<Future<Grid>>()
clusters.forEach { tasks.add(CompletableFuture.supplyAsync(it, ForkJoinPool.commonPool()).also { it.thenApply { LockSupport.unpark(thread); it } }) }
for (light in cluster.lights) {
light.red = r
light.green = g
light.blue = b
}
}*/
while (tasks.isNotEmpty()) {
tasks.removeIf {
if (it.isDone) {
val grid = it.get()
tasks.addAll(clusters)
wakeup()
while (clusters.isNotEmpty()) {
clusters.removeIf {
val grid = it.grid
// wait until thread local caches are refreshed
if (grid != null) {
if (targetMem != null) {
for (x in grid.minX - 1 .. grid.maxX) {
for (y in grid.minY - 1 .. grid.maxY) {
@ -522,10 +515,10 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
}
}
grid != null
it.isDone
}
LockSupport.parkNanos(500_000)
LockSupport.parkNanos(500_000L)
}
} else {
val grid = Grid()
@ -589,8 +582,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
var x: Double,
var y: Double,
val parent: LightCalculator,
val parentThread: Thread
) {
) : Supplier<Grid> {
val lights = ArrayList<PointLight>()
fun updateCenter() {
@ -608,13 +600,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
y /= lights.size
}
@Volatile
var grid: Grid? = null
private set
fun execute() {
if (grid != null) return
override fun get(): Grid {
val grid = parent.Grid()
for (light in lights) {
@ -635,46 +621,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
}
grid.calculateSpread()
this.grid = grid
LockSupport.unpark(parentThread)
}
}
companion object {
private val tasks = ConcurrentLinkedQueue<TaskCluster>()
private val threads = ArrayList<Thread>()
private val clusterColors = ArrayList<RGBAColor>()
private fun wakeup() {
for (thread in threads) {
LockSupport.unpark(thread)
}
}
private fun thread() {
while (true) {
val next = tasks.poll()
if (next == null) {
LockSupport.park()
continue
}
next.execute()
}
}
init {
for (i in 0 until Runtime.getRuntime().availableProcessors()) {
Thread(::thread, "Starbound Lighting Thread $i").also { threads.add(it); it.isDaemon = true }.start()
}
val rand = Random(System.nanoTime())
for (i in threads.indices) {
clusterColors.add(RGBAColor(rand.nextFloat() * 0.5f + 0.5f, rand.nextFloat() * 0.5f + 0.5f, rand.nextFloat() * 0.5f + 0.5f))
}
return grid
}
}
}