在这里插入图片描述

在接口耗时、CPU 使用率、磁盘 I/O、QPS 等指标分析中,我们经常不只关心“整体平均水平”,而是更在意:

某一小段时间内有没有出现极端高值的短时峰值
哪些窗口区间内的指标值“持续偏高”,值得重点告警或调优?

本案例基于 Kotlin Multiplatform(KMP)与 OpenHarmony,实现了一个滑动窗口峰值检测与区间最大值分析器

  • 输入一串数值序列和一个窗口大小 window
  • 使用单调双端队列在 \( O(n) \) 时间内计算每个窗口的最大值;
  • 自动找出全局峰值窗口,给出该窗口覆盖的样本区间与峰值大小;
  • 通过 ArkTS 单页面板可视化展示所有窗口的最大值列表,辅助你做短时峰值诊断。

一、问题背景与典型场景

在 AIOps 与性能诊断场景中,常见需求包括:

  1. 接口耗时短时峰值分析
    一次性能压测中,接口耗时序列整体还算平稳,但某一小段时间出现了明显抖升,需要快速定位这段时间对应的窗口区间。

  2. CPU 使用率峰值段检测
    系统大部分时间 CPU 只有 30% 左右,但偶尔会出现 3~5 秒的 90% 峰值,需要找到这些“峰值窗口”进行排查。

  3. QPS / TPS 某段时间突刺
    业务流量整体正常,但某几秒钟突然涌入流量洪峰,希望找出这些短时突刺区间,结合日志做关联分析。

这类问题常可以抽象为:

给定数值序列 \( a_0, a_1, \dots, a_{n-1} \) 和窗口大小 \( w \),
对于每个窗口 \( [i, i + w - 1] \) 计算其最大值 \( max_i \),
并找到所有 \( max_i \) 中的全局最大值及其对应窗口区间。

朴素做法是对每个窗口都重新扫描一次,时间复杂度为 \( O(nw) \),当样本很多、窗口也不小时,终端设备难以承受。
本案例采用单调队列实现,整体复杂度降低为 \( O(n) \),非常适合在 OpenHarmony 终端侧长时间运行。


二、Kotlin 滑动窗口分析引擎

1. 输入格式设计

为与前一个“有序阈值定位与二分查找分析”案例保持一致,本案例继续使用简洁的配置文本输入:

window=3
series=80,95,120,150,180,205,230,260
  • window:整数,表示滑动窗口大小(例如 3 表示每 3 个样本为一组);
  • series:一串以 , / 空格 / ; / 换行分隔的数值序列。

解析时会忽略空白字符,并自动过滤非法数值。


2. Kotlin 分析主入口

App.kt 中,我们定义了对外暴露的分析函数,并通过 @JsExport 让 OpenHarmony 侧可以直接调用:

@JsExport
fun slidingWindowMaxAnalyzer(inputData: String): String {
    val sanitized = inputData.trim()
    if (sanitized.isEmpty()) {
        return "❌ 输入为空,请按 window=3\\nseries=80,95,120,150,... 形式提供数据"
    }

    val lines = sanitized.lines()
        .map { it.trim() }
        .filter { it.isNotEmpty() }

    var windowSize: Int? = null
    var series: List<Double> = emptyList()

    for (line in lines) {
        when {
            line.startsWith("window=", ignoreCase = true) -> {
                windowSize = line.substringAfter("=").trim().toIntOrNull()
            }
            line.startsWith("series=", ignoreCase = true) -> {
                series = line.substringAfter("=")
                    .split(",", " ", ";", "\n")
                    .mapNotNull { it.trim().takeIf { s -> s.isNotEmpty() }?.toDoubleOrNull() }
            }
        }
    }

    if (windowSize == null || windowSize!! <= 0) {
        return "❌ 未找到合法的 window=... 配置,请提供大于 0 的窗口大小"
    }
    if (series.size < windowSize!!) {
        return "❌ 样本点数量 (${series.size}) 小于窗口大小 ($windowSize),无法执行滑动窗口分析"
    }

    val w = windowSize!!
    val (windowMax, peakIndex) = computeSlidingWindowMax(series, w)

    val builder = StringBuilder()
    builder.appendLine("📈 滑动窗口峰值检测与区间最大值分析报告")
    builder.appendLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    builder.appendLine("样本数量: ${series.size}")
    builder.appendLine("窗口大小: $w")
    builder.appendLine("窗口数量: ${windowMax.size}")
    builder.appendLine()

    // 全局峰值窗口
    val globalPeakValue = windowMax[peakIndex]
    builder.appendLine("🔥 全局峰值窗口")
    builder.appendLine("峰值窗口索引: $peakIndex (覆盖样本区间 [$peakIndex, ${peakIndex + w - 1}])")
    builder.appendLine("窗口内最大值: $globalPeakValue")
    builder.appendLine("窗口内样本: " +
        series.subList(peakIndex, peakIndex + w).joinToString(prefix = \"[\", postfix = \"]\"))
    builder.appendLine()

    // 所有窗口最大值列表
    builder.appendLine("🧮 所有窗口最大值列表")
    windowMax.forEachIndexed { idx, value ->
        builder.appendLine("窗口 $idx ([$idx, ${idx + w - 1}]): 最大值 = $value")
    }

    builder.appendLine()
    builder.appendLine("🧠 工程化解读")
    builder.appendLine("- 通过固定窗口滑动,可以快速观察短时间内的极端高值区间;")
    builder.appendLine("- 使用双端队列实现的线性算法,时间复杂度为 O(n),适合在终端设备侧实时运行;")
    builder.appendLine("- 可以与阈值分析、异常检测模块联动,对峰值窗口叠加更多上下文信息。")

    return builder.toString().trim()
}

可以看到,整个函数的职责非常清晰:

  • 负责解析 windowseries
  • 做必要的参数校验(窗口大小、样本数量);
  • 调用核心算法 computeSlidingWindowMax 拿到“每个窗口最大值 + 全局峰值窗口索引”;
  • 以多行文本的形式返回一份可以直接在 ArkTS Text 中展示的报告。

3. 单调队列实现滑动窗口最大值

核心算法实现如下:

private fun computeSlidingWindowMax(
    series: List<Double>,
    windowSize: Int
): Pair<List<Double>, Int> {
    // 单调队列存储“可能成为当前/未来窗口最大值”的下标,队列中的值对应的样本单调递增索引,但值单调递减
    val deque = ArrayDeque<Int>()
    val windowMax = mutableListOf<Double>()
    var globalPeakIndex = 0

    fun pushIndex(i: Int) {
        val current = series[i]
        while (deque.isNotEmpty() && series[deque.last()] <= current) {
            deque.removeLast()
        }
        deque.addLast(i)
    }

    // 初始化前 windowSize 个元素
    for (i in 0 until windowSize) {
        pushIndex(i)
    }
    windowMax += series[deque.first()]

    // 滑动窗口
    for (i in windowSize until series.size) {
        // 移除已经滑出窗口左边界的下标
        while (deque.isNotEmpty() && deque.first() <= i - windowSize) {
            deque.removeFirst()
        }
        pushIndex(i)
        windowMax += series[deque.first()]
    }

    // 找到全局峰值窗口索引
    var maxValue = windowMax[0]
    for (i in 1 until windowMax.size) {
        val v = windowMax[i]
        if (v > maxValue) {
            maxValue = v
            globalPeakIndex = i
        }
    }

    return windowMax to globalPeakIndex
}

关键思想:

  • 双端队列(Deque)中存的是“候选最大值的下标”

    • 队首永远是当前窗口的最大值下标;
    • 新元素入队前,会把队尾所有小于等于它的元素下标全部弹出(它们已经不可能再成为任何后续窗口的最大值)。
  • 窗口向右滑动时

    • 移除所有已经滑出左边界的下标(<= i - windowSize);
    • 将新元素下标按单调队列规则入队;
    • 队首对应的元素就是当前窗口的最大值。

复杂度分析:

  • 每个下标最多进队一次、出队一次,因此总操作次数为 \( O(n) \);
  • 每次窗口滑动只涉及常数次队列操作;
  • 算法整体时间复杂度 \( O(n) \),空间复杂度 \( O(n) \)(存储窗口最大值列表)。

这比朴素的 \( O(nw) \) 解法在大多数指标序列场景下要高效得多。


三、OpenHarmony 侧调用与 UI 展示思路

在 ArkTS 页面中,可以像之前的案例一样引入 Kotlin/JS 导出的函数:

import { slidingWindowMaxAnalyzer } from './hellokjs'

页面侧建议维护两个输入状态:

  • windowSize:窗口大小(可以通过数字输入框或滑块调整);
  • seriesInput:样本数据输入,多行 TextArea,支持 80,95,120...series=80,95,120... 两种形式。

调用时拼接 payload:

const seriesLine = this.seriesInput.includes('series=') ? this.seriesInput : `series=${this.seriesInput}`
const payload = `window=${this.windowSize}\n${seriesLine}`
this.result = slidingWindowMaxAnalyzer(payload)

展示区域:

  • 使用等宽字体显示 Kotlin 返回的多行分析报告;
  • 可以对“🔥 全局峰值窗口”、“窗口 k ( [i, j] ) 最大值 = …”等行做简单的颜色高亮或分组排版。

四、工程化扩展与组合玩法

基于当前滑动窗口最大值分析能力,可以进一步做很多工程级玩法:

  1. 与阈值分析组合
    在每个窗口最大值上再叠加阈值判断,标记“超过 SLO 阈值的窗口数量、比例、连续分布”等。

  2. 与异常检测/趋势分析联动
    把“滑动窗口最大值序列”作为新的时间序列输入到异常检测或趋势分段算法中,从更高维度观察峰值模式。

  3. 多窗口对比
    支持一次性输入多个窗口大小(例如 3/5/10),分别输出窗口最大值序列,帮助选择更合适的平滑窗口。

  4. 端侧实时监控
    结合 KMP + OpenHarmony 的跨端能力,将这套滑动窗口分析逻辑下沉至设备端,在没有后端支撑的场景下做轻量级实时监控与预警。

通过这个滑动窗口峰值检测案例,你可以把“算法题里的经典单调队列技巧”直接落地到 AIOps、性能调优和终端监控场景中,
在资源有限的设备上依然保持足够高效、可解释的分析能力。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐