Last active
January 11, 2023 19:31
-
-
Save lifey/05f97dcc1bbd88bf2c760b53ce0f5bbe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.management.ManagementFactory | |
import kotlin.math.abs | |
class CpuBurner(val timePeriodMs: Long, val totalCpu: Int) { | |
fun burnCpu() { | |
burnCpuInternal( totalCpu , 1 ) | |
} | |
fun burnCpuInternal( remainingPercentage: Int, idx: Int) { | |
if (remainingPercentage <= 0) return | |
val currentThreadPercentage = Math.min(remainingPercentage, 100) | |
// we want it to be a new thread so we can account the cpu consumption and that it will not be limited by thread pool capacities | |
val thread = Thread({ SingleThreadedCpuBurner().burnCpuInOneThread(timePeriodMs, currentThreadPercentage) }) | |
thread.setName("Burn CPU[${currentThreadPercentage}%/$totalCpu%]#${idx}") | |
thread.start() | |
if (remainingPercentage > 100) { | |
burnCpuInternal( remainingPercentage - 100, idx + 1) | |
} | |
thread.join() | |
} | |
} | |
class SingleThreadedCpuBurner { | |
private val CHUNK_SIZE_MS = 100L | |
private var totalSlept = 0L | |
private var totalPlanned = 0L | |
private var totalBurned = 0L | |
private var sleepSkew = 0L | |
private var blackHoleTraveler = System.currentTimeMillis() | |
fun burnCpuInOneThread(timePeriodMs: Long, percentage: Int) { | |
val threadCpuOnStart = getThreadCpuTimeNs() | |
val startTime = System.currentTimeMillis() | |
var periodMsLeft = timePeriodMs | |
while (periodMsLeft > 0) { | |
val chunk = Math.min(CHUNK_SIZE_MS, timePeriodMs) | |
burnPercentSingleIteration(chunk, percentage) | |
sleepSingleIteration(chunk,percentage) | |
periodMsLeft -= CHUNK_SIZE_MS | |
} | |
val threadCpuOnEnd = getThreadCpuTimeNs() | |
val endTime = System.currentTimeMillis() | |
val cpuConsumedMs = (threadCpuOnEnd - threadCpuOnStart).toMillis() | |
val actualPeriodMs = endTime - startTime | |
val throttling = 100.0 * (1 - | |
((cpuConsumedMs.toDouble()) / ((actualPeriodMs.toDouble()) * percentage.toDouble() / 100.0))) | |
println("${totalSlept.toMillis()} $totalPlanned ${totalBurned.toMillis()} Executed burn cpu for $actualPeriodMs ms at ${percentage}% blackhole traveler is $blackHoleTraveler CPU is ${cpuConsumedMs}ms estimated throttling ${String.format("%.2f", throttling)}%") | |
} | |
private fun getThreadCpuTimeNs(): Long { | |
val bean = ManagementFactory.getThreadMXBean() | |
val id = Thread.currentThread().id | |
return bean.getThreadCpuTime(id) | |
} | |
private fun burnPercentSingleIteration(periodMs: Long, percentage: Int) { | |
val burnPeriodMs = (periodMs * percentage) / 100 | |
if (burnPeriodMs > 0) { | |
val startTime = System.nanoTime() | |
while (System.nanoTime() - startTime < burnPeriodMs.toNanos()) { | |
for (i in 1..100000) { | |
blackHoleTraveler = blackHoleForBurnCpu(blackHoleTraveler) | |
} | |
} | |
totalBurned += System.nanoTime() - startTime | |
} | |
} | |
private fun sleepSingleIteration(periodMs: Long, percentage: Int) { | |
val sleepPeriod = (periodMs * (100 - percentage)) / 100 | |
val toSleep = sleepPeriod + sleepSkew.toMillis() | |
if (toSleep <= 0) { | |
sleepSkew += periodMs | |
return | |
} | |
val timestampOnStart = System.nanoTime() | |
Thread.sleep(toSleep) | |
val timePassed = System.nanoTime() - timestampOnStart | |
sleepSkew = (sleepSkew - sleepSkew.toMillis().toNanos()) + (toSleep.toNanos()) - timePassed | |
totalPlanned += sleepPeriod | |
totalSlept += timePassed | |
} | |
inline fun Long.toNanos() = this * 1000 * 1000 | |
inline fun Long.toMillis() = this / 1000 / 1000 | |
// the purpose of this method is to make sure that the JVM can't optimize code and eliminate it. | |
private fun blackHoleForBurnCpu(traveler: Long): Long = abs(traveler * 37 + 19) | |
} | |
fun main() { | |
for(i in 1..100) { | |
CpuBurner(10000, 20).burnCpu() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment