Misc perfromance improvements

This commit is contained in:
DBotThePony 2024-04-19 11:54:41 +07:00
parent 100afadd5c
commit fffc0b6102
Signed by: DBot
GPG Key ID: DCC23B5715498507
18 changed files with 60 additions and 47 deletions

View File

@ -4,7 +4,6 @@ import org.apache.logging.log4j.LogManager
import org.lwjgl.Version import org.lwjgl.Version
import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.server.IntegratedStarboundServer import ru.dbotthepony.kstarbound.server.IntegratedStarboundServer
import ru.dbotthepony.kstarbound.util.random.random
import java.io.File import java.io.File
import java.net.InetSocketAddress import java.net.InetSocketAddress

View File

@ -151,6 +151,12 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
@JvmField @JvmField
val COROUTINE_EXECUTOR = ExecutorWithScheduler(EXECUTOR, this).asCoroutineDispatcher() val COROUTINE_EXECUTOR = ExecutorWithScheduler(EXECUTOR, this).asCoroutineDispatcher()
// this is required for Caffeine since it ignores scheduler
// (and suffers noticeable throughput penalty) in rescheduleCleanUpIfIncomplete()
// if executor is specified as ForkJoinPool.commonPool()
@JvmField
val SCREENED_EXECUTOR: ExecutorService = object : ExecutorService by EXECUTOR {}
@JvmField @JvmField
val CLEANER: Cleaner = Cleaner.create { val CLEANER: Cleaner = Cleaner.create {
val t = Thread(it, "Starbound Global Cleaner") val t = Thread(it, "Starbound Global Cleaner")

View File

@ -345,7 +345,7 @@ class Image private constructor(
.weigher<IStarboundFile, ByteBuffer> { key, value -> value.capacity() } .weigher<IStarboundFile, ByteBuffer> { key, value -> value.capacity() }
.maximumWeight((Runtime.getRuntime().maxMemory() / 4L).coerceIn(1_024L * 1_024L * 32L /* 32 МиБ */, 1_024L * 1_024L * 256L /* 256 МиБ */)) .maximumWeight((Runtime.getRuntime().maxMemory() / 4L).coerceIn(1_024L * 1_024L * 32L /* 32 МиБ */, 1_024L * 1_024L * 256L /* 256 МиБ */))
.scheduler(Starbound) .scheduler(Starbound)
.executor(Starbound.EXECUTOR) .executor(Starbound.EXECUTOR) // SCREENED_EXECUTOR shouldn't be used here
.buildAsync(CacheLoader { .buildAsync(CacheLoader {
readImageDirect(it).data readImageDirect(it).data
}) })

View File

@ -228,7 +228,7 @@ data class BiomePlaceables(
return null // whut return null // whut
if (staticRandomDouble(x, y, blockSeed) <= blockProbability) { if (staticRandomDouble(x, y, blockSeed) <= blockProbability) {
return Placement(randomItems[staticRandomInt(0, randomItems.size, x, y, blockSeed)], Vector2i(x, y), priority) return Placement(randomItems[staticRandomInt(0, randomItems.size - 1, x, y, blockSeed)], Vector2i(x, y), priority)
} }
} else { } else {
if (weightedItems.isEmpty()) if (weightedItems.isEmpty())

View File

@ -259,7 +259,7 @@ class WorldTemplate(val geometry: WorldGeometry) {
for (layer in worldParameters.layers) { for (layer in worldParameters.layers) {
if (layer.dungeons.isNotEmpty()) { if (layer.dungeons.isNotEmpty()) {
val dungeonSpacing = geometry.size.x / layer.dungeons.size val dungeonSpacing = geometry.size.x / layer.dungeons.size
var dungeonOffset = staticRandomInt(0, geometry.size.x, seed, layer.layerBaseHeight, "dungeon") var dungeonOffset = staticRandomInt(0, geometry.size.x - 1, seed, layer.layerBaseHeight, "dungeon")
for (dungeon in layer.dungeons) { for (dungeon in layer.dungeons) {
if (dungeon.isPresent) { if (dungeon.isPresent) {
@ -303,7 +303,7 @@ class WorldTemplate(val geometry: WorldGeometry) {
.maximumSize(1_500_000L) // plentiful of space, and allows for high hit ratio (around 79%) in most situations .maximumSize(1_500_000L) // plentiful of space, and allows for high hit ratio (around 79%) in most situations
// downside is memory consumption, but why should it matter when we save 80% of cpu time? // downside is memory consumption, but why should it matter when we save 80% of cpu time?
.expireAfterAccess(Duration.ofSeconds(20)) .expireAfterAccess(Duration.ofSeconds(20))
.executor(Starbound.EXECUTOR) .executor(Starbound.SCREENED_EXECUTOR)
.scheduler(Starbound) .scheduler(Starbound)
// .recordStats() // .recordStats()
.build<Vector2i, CellInfo> { (x, y) -> cellInfo0(x, y) } .build<Vector2i, CellInfo> { (x, y) -> cellInfo0(x, y) }

View File

@ -1,27 +0,0 @@
package ru.dbotthepony.kstarbound.lua.bindings
import org.classdump.luna.ByteString
import org.classdump.luna.LuaRuntimeException
import org.classdump.luna.TableFactory
import org.classdump.luna.impl.NonsuspendableFunctionException
import org.classdump.luna.runtime.AbstractFunction1
import org.classdump.luna.runtime.ExecutionContext
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.lua.from
class AssetJsonFunction(private val tables: TableFactory) : AbstractFunction1<ByteString>() {
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
throw NonsuspendableFunctionException(this::class.java)
}
override fun invoke(context: ExecutionContext, arg1: ByteString?) {
arg1 ?: throw LuaRuntimeException("bad argument #1 to root.assetJson (string expected, got nil)")
val load = Starbound.loadJsonAsset(arg1.decode())
if (load == null) {
context.returnBuffer.setTo()
} else {
context.returnBuffer.setTo(tables.from(load))
}
}
}

View File

@ -350,7 +350,10 @@ fun provideRootBindings(lua: LuaEnvironment) {
val table = lua.newTable() val table = lua.newTable()
lua.globals["root"] = table lua.globals["root"] = table
table["assetJson"] = AssetJsonFunction(lua) table["assetJson"] = luaFunction { path: ByteString ->
returnBuffer.setTo(from(Starbound.loadJsonAsset(path.decode())))
}
table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson") table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson")
table["loadVersionedJson"] = luaStub("loadVersionedJson") table["loadVersionedJson"] = luaStub("loadVersionedJson")

View File

@ -29,6 +29,7 @@ import ru.dbotthepony.kstarbound.util.random.staticRandomLong
import ru.dbotthepony.kstarbound.util.toStarboundString import ru.dbotthepony.kstarbound.util.toStarboundString
import java.util.* import java.util.*
import java.util.random.RandomGenerator import java.util.random.RandomGenerator
import kotlin.math.sign
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()

View File

@ -1,10 +1,12 @@
package ru.dbotthepony.kstarbound.lua.bindings package ru.dbotthepony.kstarbound.lua.bindings
import org.classdump.luna.Table import org.classdump.luna.Table
import org.classdump.luna.runtime.ExecutionContext
import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.LuaEnvironment
import ru.dbotthepony.kstarbound.lua.get import ru.dbotthepony.kstarbound.lua.get
import ru.dbotthepony.kstarbound.lua.luaFunction import ru.dbotthepony.kstarbound.lua.luaFunction
import ru.dbotthepony.kstarbound.lua.set import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.lua.toPoly
import ru.dbotthepony.kstarbound.lua.toVector2d import ru.dbotthepony.kstarbound.lua.toVector2d
import ru.dbotthepony.kstarbound.world.World import ru.dbotthepony.kstarbound.world.World
@ -23,4 +25,8 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
returnBuffer.setTo(self.geometry.diff(toVector2d(arg1), toVector2d(arg2)).length) returnBuffer.setTo(self.geometry.diff(toVector2d(arg1), toVector2d(arg2)).length)
} }
} }
callbacks["polyContains"] = luaFunction { poly: Table, position: Table ->
returnBuffer.setTo(self.geometry.polyContains(toPoly(poly), toVector2d(position)))
}
} }

View File

@ -221,7 +221,7 @@ class ServerUniverse private constructor(marker: Nothing?) : Universe(), Closeab
.maximumSize(1024L) .maximumSize(1024L)
.softValues() .softValues()
.scheduler(Starbound) .scheduler(Starbound)
.executor(Starbound.EXECUTOR) .executor(Starbound.SCREENED_EXECUTOR)
.build() .build()
fun getChunkFuture(pos: Vector2i): CompletableFuture<KOptional<UniverseChunk>> { fun getChunkFuture(pos: Vector2i): CompletableFuture<KOptional<UniverseChunk>> {

View File

@ -18,6 +18,7 @@ import ru.dbotthepony.kstarbound.client.network.packets.ChunkCellsPacket
import ru.dbotthepony.kstarbound.defs.SpawnTarget import ru.dbotthepony.kstarbound.defs.SpawnTarget
import ru.dbotthepony.kstarbound.defs.WarpAction import ru.dbotthepony.kstarbound.defs.WarpAction
import ru.dbotthepony.kstarbound.defs.WorldID import ru.dbotthepony.kstarbound.defs.WorldID
import ru.dbotthepony.kstarbound.defs.world.FlyingType
import ru.dbotthepony.kstarbound.network.IPacket import ru.dbotthepony.kstarbound.network.IPacket
import ru.dbotthepony.kstarbound.network.packets.EntityCreatePacket import ru.dbotthepony.kstarbound.network.packets.EntityCreatePacket
import ru.dbotthepony.kstarbound.network.packets.EntityDestroyPacket import ru.dbotthepony.kstarbound.network.packets.EntityDestroyPacket
@ -127,6 +128,8 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
} }
} }
private var lastSeenFlyingType = FlyingType.NONE
fun tick() { fun tick() {
if (!client.worldStartAcknowledged) if (!client.worldStartAcknowledged)
return return
@ -144,11 +147,12 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
} }
run { run {
if (skyUpdateWaitTicks++ >= 20) { if (skyUpdateWaitTicks++ >= 20 || world.sky.flyingType != lastSeenFlyingType) {
val (data, version) = world.sky.networkedGroup.write(skyVersion, isLegacy = client.isLegacy) val (data, version) = world.sky.networkedGroup.write(skyVersion, isLegacy = client.isLegacy)
skyVersion = version skyVersion = version
send(EnvironmentUpdatePacket(data, ByteArrayList())) send(EnvironmentUpdatePacket(data, ByteArrayList()))
skyUpdateWaitTicks = 0 skyUpdateWaitTicks = 0
lastSeenFlyingType = world.sky.flyingType
} }
} }

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.kstarbound.util package ru.dbotthepony.kstarbound.util
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -129,7 +130,7 @@ open class BlockableEventLoop(name: String) : Thread(name), ScheduledExecutorSer
} }
if (scheduledQueue.isNotEmpty()) { if (scheduledQueue.isNotEmpty()) {
val executed = ArrayList<ScheduledTask<*>>() val executed = ObjectArrayList<ScheduledTask<*>>(4)
var lastSize: Int var lastSize: Int
do { do {

View File

@ -86,14 +86,14 @@ fun staticRandomDouble(vararg values: Any?): Double {
return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16 return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16
} }
fun staticRandomInt(origin: Int, bound: Int, vararg values: Any?): Int { fun staticRandomInt(min: Int, max: Int, vararg values: Any?): Int {
val rand = staticRandomDouble(*values) val hash = staticRandomDouble(*values)
return origin + ((bound - origin) * rand).toInt() return (min + (max - min + 1) * hash).toInt()
} }
fun staticRandomLong(origin: Long, bound: Long, vararg values: Any?): Long { fun staticRandomLong(min: Long, max: Long, vararg values: Any?): Long {
val rand = staticRandomDouble(*values) val hash = staticRandomDouble(*values)
return origin + ((bound - origin) * rand).toLong() return (min + (max - min + 1L) * hash).toLong()
} }
fun staticRandom64(vararg values: Any?): Long { fun staticRandom64(vararg values: Any?): Long {

View File

@ -90,7 +90,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
// bulk mark collision dirty of neighbour chunks as well as ours // bulk mark collision dirty of neighbour chunks as well as ours
protected fun signalChunkContentsUpdated() { protected fun signalChunkContentsUpdated() {
val signalPositions = ArrayList<Vector2i>() val signalPositions = ObjectArrayList<Vector2i>(24)
for (x in 1 .. 2) { for (x in 1 .. 2) {
for (y in 1 .. 2) { for (y in 1 .. 2) {

View File

@ -98,12 +98,18 @@ class Sky() {
} }
fun startFlying(enterHyperspace: Boolean, startInWarp: Boolean = false) { fun startFlying(enterHyperspace: Boolean, startInWarp: Boolean = false) {
if (flyingType == FlyingType.NONE)
flyingTimer = 0.0
else if (flyingType == FlyingType.WARP && warpPhase == WarpPhase.SLOWING_DOWN) {
warpPhase = WarpPhase.SPEEDING_UP
flyingTimer = speedupTime
}
if (startInWarp) if (startInWarp)
flyingType = FlyingType.WARP flyingType = FlyingType.WARP
else if (flyingType == FlyingType.NONE) else if (flyingType == FlyingType.NONE)
flyingType = FlyingType.DISEMBARKING flyingType = FlyingType.DISEMBARKING
// flyingTimer = 0.0
this.enterHyperspace = enterHyperspace this.enterHyperspace = enterHyperspace
this.startInWarp = startInWarp this.startInWarp = startInWarp
} }
@ -113,6 +119,11 @@ class Sky() {
if (skyType != SkyType.WARP) if (skyType != SkyType.WARP)
skyType = SkyType.ORBITAL skyType = SkyType.ORBITAL
if (flyingType == FlyingType.NONE || flyingType == FlyingType.ARRIVING) {
this.skyParameters = destination
this.destination = null
}
} }
private var lastFlyingType = FlyingType.NONE private var lastFlyingType = FlyingType.NONE

View File

@ -240,4 +240,13 @@ data class WorldGeometry(val size: Vector2i, val loopX: Boolean = true, val loop
fun polyDistance(poly: Poly, point: IStruct2d): Double { fun polyDistance(poly: Poly, point: IStruct2d): Double {
return poly.distance(nearestTo(poly.centre, point)) return poly.distance(nearestTo(poly.centre, point))
} }
fun split(poly: Poly): List<Poly> {
TODO()
}
fun polyContains(poly: Poly, point: IStruct2d): Boolean {
val wrap = wrap(point)
return split(poly).any { it.contains(wrap) }
}
} }

View File

@ -62,7 +62,7 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters
.softValues() .softValues()
.expireAfterAccess(Duration.ofMinutes(1)) .expireAfterAccess(Duration.ofMinutes(1))
.scheduler(Starbound) .scheduler(Starbound)
.executor(Starbound.EXECUTOR) .executor(Starbound.SCREENED_EXECUTOR)
.build<Int, Layer>(::Layer) .build<Int, Layer>(::Layer)
private inner class Sector(val sector: Vector2i) { private inner class Sector(val sector: Vector2i) {
@ -131,7 +131,7 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters
.softValues() .softValues()
.expireAfterAccess(Duration.ofMinutes(1)) .expireAfterAccess(Duration.ofMinutes(1))
.scheduler(Starbound) .scheduler(Starbound)
.executor(Starbound.EXECUTOR) .executor(Starbound.SCREENED_EXECUTOR)
.build<Vector2i, Sector>(::Sector) .build<Vector2i, Sector>(::Sector)
override fun get(x: Int, y: Int): Double { override fun get(x: Int, y: Int): Double {

View File

@ -185,7 +185,7 @@ class WormCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters)
.softValues() .softValues()
.expireAfterAccess(Duration.ofMinutes(1)) .expireAfterAccess(Duration.ofMinutes(1))
.scheduler(Starbound) .scheduler(Starbound)
.executor(Starbound.EXECUTOR) .executor(Starbound.SCREENED_EXECUTOR)
.build<Vector2i, Sector>(::Sector) .build<Vector2i, Sector>(::Sector)
override fun get(x: Int, y: Int): Double { override fun get(x: Int, y: Int): Double {