From e3eec33b2475e3e4b1a0fd771145e349ef088826 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Wed, 9 Jun 2021 16:45:30 +0300 Subject: [PATCH] Add aggressively allocating stress test for GC --- .../backend.native/tests/build.gradle | 5 ++ .../runtime/memory/stress_gc_allocations.kt | 83 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 kotlin-native/backend.native/tests/runtime/memory/stress_gc_allocations.kt diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 873e6992a8a..8ba56a7a9ae 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -2953,6 +2953,11 @@ standaloneTest("leakMemoryWithTestRunner") { } } +standaloneTest("stress_gc_allocations") { + source = "runtime/memory/stress_gc_allocations.kt" + flags = ['-tr', '-Xopt-in=kotlin.native.internal.InternalForKotlinNative'] +} + standaloneTest("mpp1") { source = "codegen/mpp/mpp1.kt" flags = ['-tr', '-Xmulti-platform'] diff --git a/kotlin-native/backend.native/tests/runtime/memory/stress_gc_allocations.kt b/kotlin-native/backend.native/tests/runtime/memory/stress_gc_allocations.kt new file mode 100644 index 00000000000..035b965a314 --- /dev/null +++ b/kotlin-native/backend.native/tests/runtime/memory/stress_gc_allocations.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +import kotlin.test.* + +import kotlin.native.concurrent.* +import kotlin.native.internal.MemoryUsageInfo + +object Blackhole { + private val hole = AtomicLong(0) + + fun consume(value: Any) { + hole.addAndGet(value.hashCode()) + } + + fun discharge() { + println(hole.value) + } +} + +// Keep a class to ensure we allocate in heap. +// TODO: Protect it from escape analysis. +class MemoryHog(val size: Int, val value: Byte, val stride: Int) { + val data = ByteArray(size) + + init { + for (i in 0 until size step stride) { + data[i] = value + } + Blackhole.consume(data) + } +} + +val peakRssBytes: Long + get() { + val value = MemoryUsageInfo.peakResidentSetSizeBytes + if (value == 0L) { + fail("Error trying to obtain peak RSS. Check if current platform is supported") + } + return value + } + +@Test +fun test() { + // One item is ~10MiB. + val size = 10_000_000 + // Total amount is ~1TiB. + val count = 100_000 + val value: Byte = 42 + // Try to make sure each page is written + val stride = 4096 + // Limit memory usage at ~200MiB + val rssDiffLimit: Long = 200_000_000 + // Trigger GC after ~100MiB are allocated + val retainLimit: Long = 100_000_000 + val progressReportsCount = 100 + + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + kotlin.native.internal.GC.thresholdAllocations = retainLimit + // Effectively disable trigger on safepoints. + kotlin.native.internal.GC.threshold = Int.MAX_VALUE + } + + // On Linux, the child process might immediately commit the same amount of memory as the parent. + // So, measure difference between peak RSS measurements. + val initialPeakRss = peakRssBytes + + for (i in 0..count) { + if (i % (count / progressReportsCount) == 0) { + println("Allocating iteration ${i + 1} of $count") + } + MemoryHog(size, value, stride) + val diffPeakRss = peakRssBytes - initialPeakRss + if (diffPeakRss > rssDiffLimit) { + // If GC does not exist, this should eventually fail. + fail("Increased peak RSS by $diffPeakRss which is more than $rssDiffLimit") + } + } + + // Make sure `Blackhole` does not get optimized out. + Blackhole.discharge() +}