Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ data class CpuTopology(val cores: List<Core>) {
return cores.filter { it.maxKhz in keep }.map { it.id }
}

/**
* The cores Auto runs on: the fastest frequency groups, adding the next group until there are 4 cores, but never the
* slowest group once there are 2 (the little cores: every thread then waits for the slowest one; on the X6, 6 and 8
* threads halved generation). 1 prime + 3 big (Dimensity 8300, Snapdragon 870): those 4. 2 + 2 + 4 (Tensor G1/G2,
* Pixel 6 and 7): the 2 + 2 fast ones, where [bigCores] keeps 2. 2 big + 6 little: the 2 big. One group: all of them.
*/
val autoCores: List<Int> get() {
val groups = cores.groupBy { it.maxKhz }.toSortedMap(compareByDescending { it }).values.toList()
val keep = ArrayList<Core>()
for ((i, group) in groups.withIndex()) {
if (keep.size >= 4 || (i == groups.lastIndex && keep.size >= 2)) break
keep += group
}
return keep.map { it.id }.sorted()
}

/** "4-7" style list for [EngineParams.cpus]. */
fun cpuList(ids: List<Int>): String = ids.sorted().joinToString(",")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ data class EngineTweaks(
fun resolve(topology: CpuTopology, totalRamBytes: Long = 0): Resolved {
val notes = ArrayList<String>()
val n = topology.cores.size.coerceAtLeast(1)
val big = topology.bigCores.ifEmpty { topology.cores.map { it.id } }
val big = topology.autoCores.ifEmpty { topology.cores.map { it.id } }
fun clamp(name: String, v: Int, lo: Int, hi: Int): Int = v.coerceIn(lo, hi).also { if (it != v) notes += "$name $v → $it (allowed $lo–$hi)" }

val coreIds: List<Int>? = when (cores) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,43 @@ class EngineTweaksTest {
assertTrue(EngineTweaks().isAuto)
}

/** Cores from (count, max kHz) groups, numbered from 0 in the order given (sysfs lists the little cores first). */
private fun soc(vararg groups: Pair<Int, Long>): CpuTopology {
var id = 0
return CpuTopology(groups.flatMap { (n, khz) -> List(n) { CpuTopology.Core(id++, khz) } })
}

@Test fun `Auto threads - 4 on the fast cores of every layout, never the little ones`() {
// Tensor G1 (Pixel 6, 6a, 6 Pro): 4x A55 1.80 + 2x A76 2.25 + 2x X1 2.80 GHz. bigCores keeps only the 2 X1.
val g1 = soc(4 to 1_803_000L, 2 to 2_253_000L, 2 to 2_802_000L)
// Tensor G2 (Pixel 7, 7a, 7 Pro): 4x A55 1.80 + 2x A78 2.35 + 2x X1 2.85 GHz.
val g2 = soc(4 to 1_803_000L, 2 to 2_348_000L, 2 to 2_850_000L)
// Snapdragon 870 (POCO F3): 4x A55 1.80 + 3x A77 2.42 + 1x A77 3.19 GHz.
val sd870 = soc(4 to 1_804_800L, 3 to 2_419_200L, 1 to 3_187_200L)
// Tensor G3 (Pixel 8): 4x A510 1.70 + 4x A715 2.45 + 1x X3 2.91 GHz.
val g3 = soc(4 to 1_704_000L, 4 to 2_450_000L, 1 to 2_910_000L)
// 6 little + 2 big (Helio G99 class).
val sixTwo = soc(6 to 2_000_000L, 2 to 2_200_000L)
// One cluster, or no frequency readable.
val flat = soc(8 to 0L)

assertEquals(listOf(6, 7), g1.bigCores)
assertEquals(listOf(4, 5, 6, 7), g1.autoCores)
assertEquals(listOf(4, 5, 6, 7), g2.autoCores)
assertEquals(listOf(4, 5, 6, 7), sd870.autoCores)
assertEquals(listOf(4, 5, 6, 7), x6.autoCores)
assertEquals(listOf(4, 5, 6, 7, 8), g3.autoCores)
assertEquals(listOf(6, 7), sixTwo.autoCores)
assertEquals((0..7).toList(), flat.autoCores)

for (t in listOf(g1, g2, sd870, x6, g3, flat)) assertEquals(4, EngineTweaks().resolve(t).params.threads)
assertEquals("4,5,6,7", EngineTweaks().resolve(g1).params.cpus)
assertEquals(2, EngineTweaks().resolve(sixTwo).params.threads)
// The user's own setting still wins.
assertEquals(2, EngineTweaks(threads = 2, cores = "6,7").resolve(g1).params.threads)
assertEquals("6,7", EngineTweaks(threads = 2, cores = "6,7").resolve(g1).params.cpus)
}

@Test fun `every knob can be set, and impossible values are clamped with a note`() {
val r = EngineTweaks(threads = 64, cores = "all", contextTokens = 100, batch = 256, microBatch = 1024, kvCache = KvType.Q8_0, flashAttention = FlashAttn.ON).resolve(x6)
assertEquals(8, r.params.threads)
Expand Down
Loading