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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
custom quantized payloads) instead of failing on the first unmapped one. Threaded through
`withPolicy`.

### Changed

- **`ExperimentalMemoryApi` opt-in gate removed** (SKEEP-003): the `sk.ainet.lang.memory` API
(`Storage`, `Scope`, `Format`, `Layout`, `TensorView`, `WeightForm`, `WeightByteOrder`, …) no
longer requires `@OptIn(ExperimentalMemoryApi::class)` to use. The annotation's own message —
"usable, but may change until milestone M1 is complete" — was stale: M0/M1/M2 all shipped
complete in 0.49.0. `ExperimentalMemoryApi` is deleted along with every `@OptIn`/
`@ExperimentalMemoryApi` annotation referencing it.

## [0.53.0] - 2026-09-02

Headline: **the export pipeline emits billion-parameter models.** Tracing a 4.5B-parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import sk.ainet.context.ExecutionContext
import sk.ainet.context.schedule.Schedule
import sk.ainet.context.withSchedule
import sk.ainet.exec.schedule.CoroutineSchedule
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.trace.RecordingTraceSink
import sk.ainet.lang.memory.trace.TraceEvent
import sk.ainet.lang.tensor.Shape
Expand All @@ -19,7 +18,6 @@ import sk.ainet.lang.types.FP32
* the trace that says which schedule ran. Every region is included verbatim into
* `tutorials/schedule-getting-started.adoc`; `SamplesTest` executes it.
*/
@OptIn(ExperimentalMemoryApi::class)
object ScheduleDemo {

class Result(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@file:OptIn(ExperimentalMemoryApi::class)

package sk.ainet.apps.plan

Expand All @@ -8,7 +7,6 @@ import kotlinx.cli.default
import sk.ainet.io.JvmRandomAccessSource
import sk.ainet.io.gguf.StreamingGGUFReader
import sk.ainet.io.gguf.planInput
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.plan.Budget
import sk.ainet.lang.memory.plan.KvCacheMode
import sk.ainet.lang.memory.plan.MemoryPlan
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
@file:OptIn(ExperimentalMemoryApi::class)

package sk.ainet.apps.plan

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.plan.Budget
import sk.ainet.lang.memory.plan.MemoryPlans
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import sk.ainet.backend.api.kernel.TernaryF32GemvKernel
import sk.ainet.backend.api.kernel.TernaryKernelPacks
import sk.ainet.bench.publish.runner.Scenario
import sk.ainet.exec.kernel.NativeTernaryF32GemvKernel
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Scope
import sk.ainet.lang.memory.Storage
import sk.ainet.lang.memory.TensorView
Expand All @@ -31,7 +30,6 @@ import kotlin.random.Random
* Dims default to the BitNet-2B FFN projection (k=2560, n=6912); the n=6912 regime also crosses
* the LUT kernel's internal pthread threshold. Primary metric: GOP/s over `2·k·n` ops.
*/
@OptIn(ExperimentalMemoryApi::class)
internal class TernaryF32GemvScenario(
smoke: Boolean,
private val providerName: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package sk.ainet.bench.publish.schema

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.trace.GenerationMetrics

/**
Expand Down Expand Up @@ -49,7 +48,6 @@ public data class GenerationMetricsRecord(
)

/** This run's metrics as the record the benchmark JSON carries. */
@OptIn(ExperimentalMemoryApi::class)
public fun GenerationMetrics.toRecord(): GenerationMetricsRecord = GenerationMetricsRecord(
prefillTokens = prefillTokens,
prefillTokensPerSecond = prefillTokensPerSecond,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sk.ainet.bench.publish.schema

import kotlinx.serialization.json.Json
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.trace.GenerationMetrics
import sk.ainet.lang.memory.trace.ModuleCost
import java.io.File
Expand All @@ -17,7 +16,6 @@ import kotlin.test.assertTrue
* The written fixture is the script's own test input: `./scripts/check_engine_json.sh
* skainet-backends/benchmarks/jvm-cpu-publish/build/engine-json-check` must pass on it.
*/
@OptIn(ExperimentalMemoryApi::class)
class GenerationMetricsRecordTest {

private val json = Json { prettyPrint = true; encodeDefaults = true; explicitNulls = false }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi

@ExperimentalMemoryApi
internal actual fun platformUseRegistry(): Boolean = System.getProperty(DispatchMode.PROPERTY) != "false"
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package sk.ainet.backend.api.kernel

import java.util.ServiceLoader
import sk.ainet.lang.memory.ExperimentalMemoryApi

/**
* Android discovery for [ViewKernelPack]. `ServiceLoader` exists on Android, so the JNI packs a
* consumer ships (e.g. the NEON row-major pack in `skainet-backend-jni-cpu`) are discovered the
* same way as on the JVM, provided the packaging step keeps `META-INF/services` entries.
*/
@ExperimentalMemoryApi
internal actual fun installPlatformKernelPacks(): List<String> =
runCatching {
ServiceLoader.load(ViewKernelPack::class.java)
Expand All @@ -21,7 +19,6 @@ internal actual fun installPlatformKernelPacks(): List<String> =
* `jvmMain`, which the Android source set does not see. Same two steps it performs — discover, then
* register, letting [KernelRegistry] sort by priority on insertion.
*/
@ExperimentalMemoryApi
internal actual fun installPlatformKernelProviders(): List<String> =
runCatching {
ServiceLoader.load(KernelProvider::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.I8Absmax
import sk.ainet.lang.memory.Storage
Expand All @@ -25,7 +24,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* The weight's codes are read once per call, not once per row: a decode step is one row against
* the whole matrix, so hoisting it is the difference between O(rows·n·k) decodes and O(n·k).
*/
@ExperimentalMemoryApi
public class BitNetGemvKernel(override val key: KernelKey) : ViewKernel {

override val name: String get() = "bitnet_gemv/reference"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi

/**
* Whether the generic (non-fast-path) matmul goes through [KernelDispatch] or the legacy
Expand All @@ -11,7 +10,6 @@ import sk.ainet.lang.memory.ExperimentalMemoryApi
* construction* instead of a `ClassCastException` (#993). The legacy path stays one flag away
* while the migration settles; it is deleted once the golden parity and benchmark evidence is in.
*/
@ExperimentalMemoryApi
public object DispatchMode {
/** Set to `false` (`skainet.dispatch.registry=false`) to force the legacy generic fallback. */
public const val PROPERTY: String = "skainet.dispatch.registry"
Expand All @@ -24,5 +22,4 @@ public object DispatchMode {
}

/** Platform reading of [DispatchMode.PROPERTY]; defaults to `true` where there is no property store. */
@ExperimentalMemoryApi
internal expect fun platformUseRegistry(): Boolean
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.BlockOrder
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.Scope
import sk.ainet.lang.memory.TensorView
Expand All @@ -23,7 +22,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* Adapters allocate in the caller's [Scope] (a `Forward` scope in a generation loop) and are
* emitted as [TraceEvent.AdapterInserted] — the "hidden 12 GB" of #782 becomes a visible event.
*/
@ExperimentalMemoryApi
public object KernelDispatch {

/**
Expand Down Expand Up @@ -255,7 +253,6 @@ public object KernelDispatch {
}

/** A view of the same contiguous bytes under a different shape (rule 5: reshape is a view). */
@ExperimentalMemoryApi
public fun TensorView.reshapeContiguous(newShape: Shape): TensorView {
require(isContiguous) { "reshape needs a contiguous view" }
require(newShape.volume.toLong() == elementCount) { "reshape must keep the element count ($elementCount), got ${newShape.volume}" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.TensorView
import sk.ainet.lang.tensor.storage.TensorEncoding
Expand All @@ -14,7 +13,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* Keys are values: equal keys select the same kernel, and a key prints as something a log or an
* `UnsupportedKernel` message can show: `matmul(F32/Dense(4B) contiguous × F32/Q4_K blocked) @host`.
*/
@ExperimentalMemoryApi
public data class KernelKey(
val op: String,
val operands: List<OperandKey>,
Expand Down Expand Up @@ -43,7 +41,6 @@ public data class KernelKey(
}

/** One operand of a [KernelKey]: its [Format] plus the layout class the kernel must cope with. */
@ExperimentalMemoryApi
public data class OperandKey(val format: Format, val layout: LayoutClass) {
override fun toString(): String = "$format ${layout.name.lowercase()}"

Expand Down Expand Up @@ -77,7 +74,6 @@ public data class OperandKey(val format: Format, val layout: LayoutClass) {
* orders, and #973 is what happens when that is left implicit. A kernel declares which one it
* takes, and the dispatcher relayouts when the operand disagrees.
*/
@ExperimentalMemoryApi
public enum class LayoutClass {
CONTIGUOUS,
STRIDED,
Expand All @@ -94,15 +90,13 @@ public enum class LayoutClass {
}

/** Thrown when no registered kernel and no adapter chain can serve a key; lists what is registered. */
@ExperimentalMemoryApi
public class UnsupportedKernelException(
public val key: KernelKey,
public val candidates: List<String>,
message: String = "No kernel for $key" + if (candidates.isEmpty()) "" else "; registered: ${candidates.joinToString(", ")}",
) : IllegalArgumentException(message)

/** The encoding name a [KernelKey] uses for a format, matching `KernelProvider.supports`' dtype keys. */
@ExperimentalMemoryApi
public val Format.kernelEncodingName: String
get() = when (val e = encoding) {
is TensorEncoding.Dense -> dtype.name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.Storage
import sk.ainet.lang.memory.TensorView
Expand All @@ -21,7 +20,6 @@ import sk.ainet.lang.types.FP32
* the dispatcher relayouts. That distinction is what #1029 was missing and what made mixing the two
* a silent-wrong-numbers bug rather than a crash (#968, #971).
*/
@ExperimentalMemoryApi
public object KernelPacks {

/** Capability marker for a provider that needs an explicit vector unit (Panama, NEON, …). */
Expand Down Expand Up @@ -81,7 +79,6 @@ public object KernelPacks {
* output-major (`[n, k]`, the shape SKaiNET's dispatch normalises to). Unwraps each view once —
* per the Phase-2 spike (#1016) — and calls the pack's strided GEMM.
*/
@ExperimentalMemoryApi
public class Fp32ViewMatmulKernel(
providerName: String,
private val kernel: Fp32MatmulKernel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi

/**
* Marks a [ViewKernel] that serves its `BLOCKED_ROW_MAJOR` weight operand straight from off-heap
Expand All @@ -17,5 +16,4 @@ import sk.ainet.lang.memory.ExperimentalMemoryApi
* so a kernel gaining or losing this marker without updating them fails CI instead of drifting
* silently.
*/
@ExperimentalMemoryApi
public interface MappedCapableKernel
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.TensorView

Expand All @@ -9,7 +8,6 @@ import sk.ainet.lang.memory.TensorView
* *Kernel*, §5.2). Custom kernels are written against views and registered — an author never
* touches a `TensorData` subclass.
*/
@ExperimentalMemoryApi
public interface ViewKernel {
/** What this kernel serves. */
public val key: KernelKey
Expand Down Expand Up @@ -39,7 +37,6 @@ public interface ViewKernel {
* `out = a × bᵀ` in the shapes SKaiNET's dispatch normalises to: `a` is `[m, k]`, `b` is `[n, k]`
* (a weight stored output-major, as GGUF does), `out` is `[m, n]`.
*/
@ExperimentalMemoryApi
public class ReferenceMatmulKernel(override val key: KernelKey) : ViewKernel {
override val name: String get() = "reference"

Expand All @@ -61,7 +58,6 @@ public class ReferenceMatmulKernel(override val key: KernelKey) : ViewKernel {

public companion object {
/** The reference kernel for the formats of [a] and [b]. */
@ExperimentalMemoryApi
public fun forOperands(a: Format, b: Format): ReferenceMatmulKernel =
ReferenceMatmulKernel(KernelKey("matmul", listOf(OperandKey.contiguous(a), OperandKey.contiguous(b))))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.blockSpec
import sk.ainet.lang.tensor.storage.TensorEncoding

Expand All @@ -21,7 +20,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* Every fixture is **three blocks wide** on purpose. At one block per row the two orders coincide,
* which is exactly the shape that hid #968.
*/
@ExperimentalMemoryApi
public object PackedLayoutFixtures {

/** The formats the contract covers — everything with a block geometry and a matmul kernel. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.BlockOrder
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.Storage
import sk.ainet.lang.memory.TensorView
Expand All @@ -20,7 +19,6 @@ import sk.ainet.lang.types.FP32
* One row at a time: the SPI is a matrix-vector kernel, so an activation of `m` rows is `m` calls,
* which is what the decode path does anyway (`m == 1`).
*/
@ExperimentalMemoryApi
public class PackedViewMatmulKernel(
providerName: String,
private val encodingName: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.BlockOrder
import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Scope
import sk.ainet.lang.memory.blockSpec
import sk.ainet.lang.memory.TensorView
Expand All @@ -23,7 +22,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* The relayout is O(bytes). Call it **once, at load**; a weight prepacked at load hits the packed
* kernel's key directly and the dispatcher copies nothing per call (#1095).
*/
@ExperimentalMemoryApi
public object PackedWeights {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.plan.KernelCapabilities
import sk.ainet.lang.tensor.storage.TensorEncoding

Expand All @@ -26,7 +25,6 @@ import sk.ainet.lang.tensor.storage.TensorEncoding
* CPU cannot feed anything, whatever it declares. Dispatch kernels carry their requirements in
* their key's capability set instead, and are filtered when they are selected.
*/
@ExperimentalMemoryApi
public object RegistryKernelCapabilities : KernelCapabilities {

/** The activation dtype every packed matmul kernel in the tree takes. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sk.ainet.backend.api.kernel

import sk.ainet.lang.memory.ExperimentalMemoryApi
import sk.ainet.lang.memory.Format
import sk.ainet.lang.memory.Storage
import sk.ainet.lang.memory.TensorView
Expand Down Expand Up @@ -30,7 +29,6 @@ import sk.ainet.lang.types.FP32
* row-major order. Output `[rows, n]` FP32. The per-tensor scale is applied to the output —
* native implementations ([TernaryF32GemvNative]) do not see it.
*/
@ExperimentalMemoryApi
public class TernaryF32GemvKernel(override val key: KernelKey) : ViewKernel {

override val name: String get() = "ternary_f32_gemv/reference"
Expand Down
Loading
Loading