Abrarul8541 commited on
Commit
b616d3a
·
verified ·
1 Parent(s): eff296f

Phase 3: device profiler and tier matrix

Browse files
app/src/main/java/io/github/abrarul/modularai/model/DeviceProfiler.kt ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package io.github.abrarul.modularai.model
2
+
3
+ import android.app.ActivityManager
4
+ import android.content.Context
5
+ import android.content.pm.PackageManager
6
+ import android.os.Build
7
+ import android.os.Environment
8
+ import android.os.StatFs
9
+ import java.io.File
10
+ import kotlin.system.measureTimeMillis
11
+
12
+ enum class Tier(val label: String, val sizes: String) {
13
+ ULTRA_LIGHT("Ultra-Light", "1B-3B"),
14
+ BALANCED("Balanced", "7B-8B"),
15
+ POWER("Power", "14B+")
16
+ }
17
+
18
+ data class Profile(
19
+ val soc: String,
20
+ val abi: String,
21
+ val cores: Int,
22
+ val totalRamMb: Long,
23
+ val availRamMb: Long,
24
+ val freeStorageMb: Long,
25
+ val vulkan: Boolean,
26
+ val storageMBps: Double,
27
+ val tier: Tier
28
+ )
29
+
30
+ data class Recommendation(
31
+ val tier: Tier,
32
+ val maxParamsB: Double,
33
+ val quant: String,
34
+ val contextTokens: Int,
35
+ val estTokensPerSec: String
36
+ )
37
+
38
+ object DeviceProfiler {
39
+
40
+ fun scan(ctx: Context): Profile {
41
+ val am = ctx.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
42
+ val mi = ActivityManager.MemoryInfo().also { am.getMemoryInfo(it) }
43
+ val total = mi.totalMem / 1_048_576
44
+ val stat = StatFs(Environment.getDataDirectory().path)
45
+ val soc = if (Build.VERSION.SDK_INT >= 31) "${Build.SOC_MANUFACTURER} ${Build.SOC_MODEL}" else Build.HARDWARE
46
+ return Profile(
47
+ soc = soc,
48
+ abi = Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown",
49
+ cores = Runtime.getRuntime().availableProcessors(),
50
+ totalRamMb = total,
51
+ availRamMb = mi.availMem / 1_048_576,
52
+ freeStorageMb = stat.availableBytes / 1_048_576,
53
+ vulkan = ctx.packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL),
54
+ storageMBps = benchStorage(ctx.cacheDir),
55
+ tier = when {
56
+ total < 5_000 -> Tier.ULTRA_LIGHT
57
+ total < 10_000 -> Tier.BALANCED
58
+ else -> Tier.POWER
59
+ }
60
+ )
61
+ }
62
+
63
+ /** 32 MB sequential write benchmark. */
64
+ private fun benchStorage(dir: File): Double {
65
+ val f = File(dir, "bench.tmp")
66
+ val buf = ByteArray(1 shl 20)
67
+ val ms = measureTimeMillis {
68
+ f.outputStream().use { o -> repeat(32) { o.write(buf) }; o.fd.sync() }
69
+ }
70
+ f.delete()
71
+ return if (ms == 0L) 0.0 else 32_000.0 / ms
72
+ }
73
+
74
+ /**
75
+ * Heuristic: a Q4_K_M GGUF needs roughly 0.6 GB per billion params plus KV cache.
76
+ * Uses ~60% of currently available RAM as the budget. Tokens/s is a rough estimate only;
77
+ * real numbers come from the in-app benchmark.
78
+ */
79
+ fun recommend(p: Profile): Recommendation {
80
+ val budgetGb = p.availRamMb * 0.6 / 1024.0
81
+ val maxB = ((budgetGb - 0.5) / 0.6).coerceAtLeast(0.5)
82
+ val quant = when {
83
+ maxB >= 8 -> "Q5_K_M"
84
+ maxB >= 3 -> "Q4_K_M"
85
+ maxB >= 1.5 -> "Q4_K_S"
86
+ else -> "IQ2_XS"
87
+ }
88
+ val ctxTokens = when {
89
+ budgetGb >= 8 -> 8192
90
+ budgetGb >= 4 -> 4096
91
+ else -> 2048
92
+ }
93
+ val tps = when (p.tier) {
94
+ Tier.ULTRA_LIGHT -> "3-10"
95
+ Tier.BALANCED -> "2-8"
96
+ Tier.POWER -> "3-12"
97
+ }
98
+ return Recommendation(p.tier, maxB, quant, ctxTokens, tps)
99
+ }
100
+ }