在这里插入图片描述

每日一句正能量

看清楚哪些是无法改变的,然后停止无谓的内耗。
很多痛苦源于试图控制不可控之事:别人的态度、过去的选择、命运的无常。智慧不是去战胜一切,而是分辨“可控”与“不可控”,然后把能量用在能改变的地方,对无法改变的轻轻放手。

前言

在嵌入式系统开发中,性能优化往往是一场"盲人摸象"——代码能跑通,但不知道瓶颈在哪;改了一处代码,不确定是否真的变快了。PC 端开发者习惯使用 gprof、Valgrind 等成熟工具进行性能分析,但在资源受限、无操作系统的裸机环境中,这些工具完全无法工作。本文将系统阐述嵌入式场景下 gprof 的局限性,并深入讲解基于 ARM Cortex-M DWT(Data Watchpoint and Trace)周期计数器的运行时 Profiling 方案,从原理到实战,构建一套完整的嵌入式性能分析基础设施。


一、gprof 的局限与嵌入式 Profiling 的特殊性

1.1 gprof 的工作原理与前提条件

gprof 是 GNU 工具链提供的性能分析工具,其工作依赖于两个前提 :

  1. 编译时插桩:通过 -pg 编译选项,编译器在每个函数入口和出口自动插入 mcount 调用,记录调用关系和执行时间
  2. 运行时文件系统:程序退出时生成 gmon.out 文件,后续通过 gprof 命令解析
# gprof标准使用流程(仅适用于Linux/PC)
gcc -Wall -g -pg -o example example.c
./example          # 运行后生成 gmon.out
gprof example gmon.out > report.txt

1.2 为什么 gprof 不适合嵌入式

gprof 需求 嵌入式现实 结论
-pg 编译选项 需要 libc_p 库,嵌入式通常使用 newlib-nano ❌ 不支持
文件系统写入 gmon.out 裸机无文件系统,Flash 写入寿命有限 ❌ 不可行
程序正常退出 嵌入式程序为无限循环,不会"退出" ❌ 不适用
函数级插桩开销 每个函数增加约 10-20 条指令,实时系统无法接受 ❌ 侵入性太高
中断/ISR 分析 gprof 无法统计中断服务程序 ❌ 缺失关键数据

嵌入式 Profiling 的核心诉求

  • 零侵入或极低侵入:不能影响实时性
  • 硬件级精度:纳秒级时间测量
  • 中断友好:能分析 ISR 执行时间
  • 实时输出:无需等待程序结束
  • 资源占用小:RAM/Flash 开销 < 1KB

在这里插入图片描述


二、DWT 周期计数器:ARM Cortex-M 的"性能显微镜"

2.1 DWT 架构与寄存器详解

DWT(Data Watchpoint and Trace)是 ARM CoreSight 调试架构的核心组件,所有 Cortex-M3/M4/M7/M33/M55 内核均内置。其 32 位周期计数器 CYCCNT 以 CPU 主频为基准递增,无需软件干预即可实现纳秒级时间测量 。

寄存器 地址 功能
DEMCR 0xE000EDFC 调试异常与监控控制寄存器,bit24(TRCENA)使能跟踪
DWT_CTRL 0xE0001000 DWT 控制寄存器,bit0(CYCCNTENA)使能周期计数
DWT_CYCCNT 0xE0001004 32位周期计数器,每 CPU 时钟周期 +1
DWT_COMP0~3 0xE0001020+ 4 组比较寄存器,匹配时触发中断/事件
DWT_FUNCTION0~3 0xE0001028+ 比较功能配置(数据匹配/PC采样/周期计数)

精度计算

  • STM32F407 @ 168MHz:1 周期 = 5.95ns
  • STM32H743 @ 480MHz:1 周期 = 2.08ns
  • nRF52840 @ 64MHz:1 周期 = 15.625ns

2.2 DWT 初始化与基础测量

#include "core_cm4.h"  /* CMSIS 标准头文件 */

/**
 * @brief DWT周期计数器初始化
 * @note  必须在系统时钟配置完成后调用
 */
void dwt_init(void) {
    /* 1. 使能DWT跟踪功能 (TRCENA = 1) */
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    
    /* 2. 清零周期计数器 */
    DWT->CYCCNT = 0;
    
    /* 3. 使能周期计数 (CYCCNTENA = 1) */
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

/**
 * @brief 获取当前周期数 (内联,零开销)
 */
static inline uint32_t dwt_get_cycles(void) {
    return DWT->CYCCNT;
}

/**
 * @brief 周期数转微秒
 */
static inline float dwt_to_us(uint32_t cycles) {
    return cycles / (SystemCoreClock / 1e6f);
}

/**
 * @brief 测量函数执行时间
 */
void measure_function(void (*fn)(void)) {
    uint32_t start = dwt_get_cycles();
    fn();
    uint32_t end = dwt_get_cycles();
    float us = dwt_to_us(end - start);
    printf("Function took %.2f us (%lu cycles)\r\n", us, end - start);
}

2.3 RTOS 环境下的精确测量

在 FreeRTOS 等 RTOS 环境中,任务切换和中断会干扰测量结果。需要在关键段禁用中断保护 :

#include "FreeRTOS.h"
#include "task.h"

void vTaskFunction(void *pvParameters) {
    uint32_t start, end;
    
    /* 进入临界区,禁止任务切换和中断 */
    taskENTER_CRITICAL();
    start = DWT->CYCCNT;
    
    /* 被测代码:算法核心路径 */
    process_sensor_data();
    
    end = DWT->CYCCNT;
    taskEXIT_CRITICAL();
    
    uint32_t cycles = end - start;
    float us = dwt_to_us(cycles);
    
    /* 输出到日志或ITM */
    LOG_INFO("Processing took %.2f us", us);
}

2.4 DWT 比较器的高级应用

DWT 提供 4 组比较器,可用于断点触发周期性采样

/**
 * @brief 配置DWT比较器:当执行到指定函数时触发中断
 */
void dwt_set_function_breakpoint(void *func_addr, uint32_t comp_id) {
    /* 确保比较器编号合法 (0-3) */
    if (comp_id > 3) return;
    
    volatile uint32_t *comp_reg = &DWT->COMP0 + comp_id * 2;
    volatile uint32_t *func_reg = &DWT->FUNCTION0 + comp_id * 2;
    
    *comp_reg = (uint32_t)func_addr;     /* 设置比较地址 */
    *func_reg = 0x00000001;              /* 功能:PC匹配触发 */
    
    /* 使能DWT中断 (通过NVIC) */
    NVIC_EnableIRQ(DebugMonitor_IRQn);
}

/**
 * @brief DebugMonitor中断处理:记录命中次数
 */
void DebugMon_Handler(void) {
    g_dwt_hit_count++;
    /* 清除DWT中断标志 */
    DWT->FUNCTION0 &= ~0x01;
}

在这里插入图片描述


三、热点分析框架:从零构建自动统计系统

3.1 框架设计目标

一个实用的嵌入式热点分析框架需要满足:

  • 自动统计:通过宏自动插入打点,无需手动修改每个函数
  • 多维度指标:总时间、调用次数、最小/最大/平均时间
  • 嵌套支持:正确处理函数调用链(A→B→C)
  • 低开销:单次打点 < 10 个时钟周期
  • 灵活输出:支持 UART/ITM/RTT 等多种输出通道

3.2 核心框架实现

#include <string.h>
#include <stdio.h>

#define MAX_PROFILE_ENTRIES 32
#define PROFILE_ENABLED     1

/**
 * @brief 性能分析条目
 */
typedef struct {
    const char *name;           /* 函数名 */
    uint32_t    total_cycles;   /* 总执行周期 */
    uint32_t    call_count;     /* 调用次数 */
    uint32_t    min_cycles;     /* 最小周期 */
    uint32_t    max_cycles;     /* 最大周期 */
    uint32_t    depth;          /* 当前嵌套深度 */
} profile_entry_t;

static profile_entry_t g_profile_table[MAX_PROFILE_ENTRIES];
static uint32_t        g_profile_count = 0;
static uint32_t        g_profile_stack[8];    /* 调用栈,支持8层嵌套 */
static uint32_t        g_profile_sp = 0;     /* 栈指针 */

/**
 * @brief 查找或创建条目
 */
static int profile_find_or_create(const char *name) {
    for (int i = 0; i < g_profile_count; i++) {
        if (strcmp(g_profile_table[i].name, name) == 0) {
            return i;
        }
    }
    /* 新建条目 */
    int idx = g_profile_count++;
    g_profile_table[idx].name = name;
    g_profile_table[idx].min_cycles = 0xFFFFFFFF;  /* 初始化为最大值 */
    return idx;
}

/**
 * @brief 进入函数
 */
void profile_enter(const char *name) {
    int idx = profile_find_or_create(name);
    g_profile_table[idx].call_count++;
    g_profile_table[idx].depth++;
    g_profile_stack[g_profile_sp++] = idx;
}

/**
 * @brief 退出函数
 */
void profile_exit(const char *name, uint32_t start_cycles) {
    uint32_t cycles = dwt_get_cycles() - start_cycles;
    int idx = g_profile_stack[--g_profile_sp];
    
    g_profile_table[idx].total_cycles += cycles;
    if (cycles < g_profile_table[idx].min_cycles) {
        g_profile_table[idx].min_cycles = cycles;
    }
    if (cycles > g_profile_table[idx].max_cycles) {
        g_profile_table[idx].max_cycles = cycles;
    }
    g_profile_table[idx].depth--;
}

/**
 * @brief 便捷宏:自动打点
 */
#define PROFILE_BEGIN(name) \
    uint32_t _prof_start_##name = dwt_get_cycles(); \
    profile_enter(#name)

#define PROFILE_END(name) \
    profile_exit(#name, _prof_start_##name)

3.3 使用示例与输出报告

/* 在业务函数中插入打点 */
void sensor_process(void) {
    PROFILE_BEGIN(sensor_process);
    
    PROFILE_BEGIN(adc_read);
    adc_sample();
    PROFILE_END(adc_read);
    
    PROFILE_BEGIN(filter);
    kalman_filter();
    PROFILE_END(filter);
    
    PROFILE_BEGIN(encode);
    data_encode();
    PROFILE_END(encode);
    
    PROFILE_END(sensor_process);
}

/* 周期结束时输出报告 */
void profile_report(void) {
    printf("\r\n=== Performance Report ===\r\n");
    printf("%-20s %8s %10s %10s %10s\r\n",
           "Function", "Calls", "Total(us)", "Avg(us)", "Max(us)");
    
    for (int i = 0; i < g_profile_count; i++) {
        float total_us = dwt_to_us(g_profile_table[i].total_cycles);
        float avg_us = total_us / g_profile_table[i].call_count;
        float max_us = dwt_to_us(g_profile_table[i].max_cycles);
        
        printf("%-20s %8lu %10.2f %10.2f %10.2f\r\n",
               g_profile_table[i].name,
               g_profile_table[i].call_count,
               total_us, avg_us, max_us);
    }
}

典型输出报告

=== Performance Report ===
Function                 Calls   Total(us)    Avg(us)    Max(us)
sensor_process              100     5234.56      52.35     89.12
  ├─ adc_read             100     3120.00      31.20     45.60   <-- 热点1 (59.6%)
  ├─ filter               100     1456.78      14.57     22.30   <-- 热点2 (27.8%)
  └─ encode               100      657.78       6.58     12.40
uart_send                  50     1890.00      37.80     52.10
i2c_transfer               80     1240.00      15.50     28.90

Hotspot Analysis:
  1. adc_read() 占 sensor_process 59.6% -> 建议: 改用DMA采样
  2. filter() 占 sensor_process 27.8% -> 建议: 查表法替代浮点运算
  3. uart_send() 平均37.8us -> 建议: 环形缓冲区+中断发送

在这里插入图片描述


四、ITM/SWO 实时流输出:从 MCU 到 PC 的"数据高速公路"

4.1 ITM 与 SWO 原理

ITM(Instrumentation Trace Macrocell)是 Cortex-M 内核的跟踪单元,通过 SWO(Serial Wire Output)引脚将数据以串行方式输出到调试器。相比 UART 日志,SWO 的优势在于 :

  • 不占用 UART 外设:SWO 使用专用引脚,不影响正常通信
  • 高带宽:最高支持 CPU 时钟的 1/4(480MHz 下可达 120Mbps)
  • 多通道:32 个独立通道,可同时输出日志、事件、性能数据
  • 时间戳:自动附加 DWT 周期计数器时间戳

4.2 ITM 配置与数据发送

#define ITM_PORT    ((volatile uint32_t *)0xE0000000)
#define ITM_TER0    (*(volatile uint32_t *)0xE0000E00)
#define ITM_TCR     (*(volatile uint32_t *)0xE0000E80)

/**
 * @brief ITM跟踪端口初始化
 */
void itm_init(void) {
    /* 使能ITM */
    ITM_TCR |= (1 << 0);    /* ITMENA */
    ITM_TER0 |= (1 << 0);   /* 使能端口0 */
    
    /* 配置SWO输出波特率 */
    /* SWO_CLK = CPU_FREQ / (SWO_PRESCALER + 1) */
    uint32_t prescaler = SystemCoreClock / 2000000 - 1;
    *((volatile uint32_t *)0xE0040010) = prescaler;
}

/**
 * @brief 发送性能事件到ITM
 * @param type 0x01=函数进入 0x02=函数退出
 * @param name 函数名
 */
void itm_send_event(uint8_t type, const char *name) {
    /* 等待端口就绪 */
    while (ITM_PORT[0] == 0);
    
    /* 发送事件类型 */
    ITM_PORT[0] = type;
    
    /* 发送函数名长度和内容 */
    uint8_t len = strlen(name);
    ITM_PORT[0] = len;
    for (int i = 0; i < len; i++) {
        ITM_PORT[0] = name[i];
    }
    
    /* 发送DWT周期数(4字节) */
    uint32_t cycles = DWT->CYCCNT;
    ITM_PORT[0] = (cycles >> 24) & 0xFF;
    ITM_PORT[0] = (cycles >> 16) & 0xFF;
    ITM_PORT[0] = (cycles >> 8)  & 0xFF;
    ITM_PORT[0] = cycles & 0xFF;
}

4.3 PC 端数据捕获与火焰图生成

步骤 1:使用 OpenOCD 捕获 SWO 数据

# 启动OpenOCD并配置SWO捕获
openocd -f interface/stlink.cfg \
        -f target/stm32h7x.cfg \
        -c "tpiu config internal swo.log uart off 2000000"

步骤 2:Python 脚本解析并生成火焰图格式

#!/usr/bin/env python3
# parse_swo.py - SWO数据解析与火焰图生成

import struct
import sys

def parse_swo_log(filename):
    """解析SWO日志,重建函数调用时间线"""
    events = []
    with open(filename, 'rb') as f:
        while True:
            header = f.read(2)
            if len(header) < 2:
                break
            event_type = header[0]
            name_len = header[1]
            name = f.read(name_len).decode('utf-8')
            cycles = struct.unpack('>I', f.read(4))[0]
            events.append({
                'type': event_type,  # 1=enter, 2=exit
                'name': name,
                'cycles': cycles
            })
    return events

def generate_folded(events):
    """生成火焰图folded格式: func1;func2;func3 count"""
    stack = []
    output = []
    
    for evt in events:
        if evt['type'] == 1:  # enter
            stack.append(evt['name'])
        elif evt['type'] == 2:  # exit
            if stack:
                # 计算该次调用的周期数
                path = ';'.join(stack)
                output.append(f"{path} 1")
                stack.pop()
    
    return '\n'.join(output)

if __name__ == '__main__':
    events = parse_swo_log(sys.argv[1])
    folded = generate_folded(events)
    with open('profile.folded', 'w') as f:
        f.write(folded)

步骤 3:使用 flamegraph.pl 生成可视化火焰图

# 下载Brendan Gregg的火焰图工具
git clone https://github.com/brendangregg/FlameGraph.git

# 生成SVG火焰图
./FlameGraph/flamegraph.pl profile.folded > profile.svg

# 浏览器打开查看
firefox profile.svg

在这里插入图片描述


五、实战案例:无人机飞控 PID 循环优化

5.1 问题背景

某无人机飞控系统基于 STM32H7(480MHz),PID 控制循环执行时间波动达 200μs,导致控制频率不稳定(目标 5KHz,实际 3-5KHz 波动)。通过 DWT 热点分析定位瓶颈 。

5.2 优化前热点分析

void flight_control_loop(void) {
    PROFILE_BEGIN(flight_control_loop);
    
    /* IMU数据读取与融合 */
    PROFILE_BEGIN(imu_read);
    imu_read_and_fuse();
    PROFILE_END(imu_read);
    
    /* 姿态解算:四元数+欧拉角转换 */
    PROFILE_BEGIN(attitude_solve);
    attitude_calculate();
    PROFILE_END(attitude_solve);
    
    /* PID计算:3轴角速度环 */
    PROFILE_BEGIN(pid_compute);
    for (int axis = 0; axis < 3; axis++) {
        pid_output[axis] = pid_compute(&pid[axis], 
            setpoint[axis], feedback[axis]);
    }
    PROFILE_END(pid_compute);
    
    /* 电机输出 */
    PROFILE_BEGIN(motor_output);
    motor_mix_and_output();
    PROFILE_END(motor_output);
    
    PROFILE_END(flight_control_loop);
}

测量结果

函数 耗时 (μs) 占比 分析
attitude_solve() 90 45% sin()/cos() 浮点运算
pid_compute() 45 22.5% sqrt() 开方运算
imu_read() 30 15% 轮询 SPI 读取
motor_output() 20 10% 普通计算
其他 15 7.5%

5.3 分步优化

优化 1:三角函数查表法

/* 优化前:每次调用 sin() 约 30μs */
float roll_angle = asin(2*(q0*q1 + q2*q3));

/* 优化后:预计算 4096 点查表,0.5μs */
#define SIN_TABLE_SIZE 4096
static const float sin_table[SIN_TABLE_SIZE] = { /* 预计算 */ };

static inline float fast_sin(float rad) {
    int idx = (int)((rad / (2*M_PI)) * SIN_TABLE_SIZE) & (SIN_TABLE_SIZE - 1);
    return sin_table[idx];
}

优化 2:快速平方根(Q_rsqrt)

/* 优化前:sqrt() 约 15μs */
float magnitude = sqrt(x*x + y*y + z*z);

/* 优化后:牛顿迭代法,2μs */
float fast_sqrt(float x) {
    union { float f; uint32_t i; } u = {x};
    u.i = 0x5f375a86 - (u.i >> 1);  /* 初始猜测 */
    u.f *= 1.5f - 0.5f * x * u.f * u.f;  /* 一次迭代 */
    return u.f * x;
}

优化 3:SPI 轮询改 DMA

/* 优化前:轮询读取,阻塞30μs */
HAL_SPI_Receive(&hspi1, imu_buf, 14, 100);

/* 优化后:DMA非阻塞,仅1μs启动开销 */
HAL_SPI_Receive_DMA(&hspi1, imu_buf, 14);
/* 在DMA完成中断中处理数据 */

优化 4:CMSIS-DSP 库加速

/* 优化前:手写矩阵乘法 */
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        result[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j];
    }
}

/* 优化后:ARM NEON/FPU 加速 */
arm_mat_mult_f32(&mat_a, &mat_b, &mat_result);

5.4 优化效果

指标 优化前 优化后 提升
PID 循环时间 200μs 80μs 2.5x
控制频率 5KHz (波动) 12.5KHz (稳定) 2.5x
sin() 耗时 30μs 0.5μs 60x
sqrt() 耗时 15μs 2μs 7.5x
SPI 读取耗时 30μs 1μs 30x
系统响应延迟 降低 60%

在这里插入图片描述


六、进阶技巧:多维度性能监控

6.1 中断响应时间测量

/**
 * @brief 测量中断响应延迟
 * @note  在GPIO中断中测量从信号触发到ISR首条指令的时间
 */
volatile uint32_t g_irq_latency = 0;

void EXTI_IRQHandler(void) {
    uint32_t enter_cycles = DWT->CYCCNT;  /* ISR入口立即采样 */
    
    /* 计算延迟:GPIO翻转时刻到ISR入口 */
    /* GPIO翻转时刻由外部逻辑分析仪记录,或通过另一个DWT比较器捕获 */
    g_irq_latency = enter_cycles - g_gpio_trigger_cycles;
    
    /* 处理中断... */
}

6.2 DMA 与 CPU 争用分析

/**
 * @brief 测量DMA传输期间的CPU性能下降
 */
void dma_cpu_contention_test(void) {
    /* 基线:纯CPU计算 */
    uint32_t cpu_only = measure_cpu_benchmark();
    
    /* 启动DMA传输 */
    HAL_DMA_Start_IT(&hdma, src, dst, size);
    
    /* 同时运行相同计算 */
    uint32_t cpu_with_dma = measure_cpu_benchmark();
    
    /* 计算争用导致的性能下降 */
    float degradation = (1.0f - (float)cpu_with_dma / cpu_only) * 100;
    printf("DMA contention: %.1f%% performance loss\r\n", degradation);
}

6.3 功耗与性能权衡

/**
 * @brief 不同主频下的性能/功耗测量
 */
void cpufreq_profile(void) {
    const uint32_t freqs[] = {480, 240, 120, 60};  /* MHz */
    
    for (int i = 0; i < 4; i++) {
        set_system_clock(freqs[i]);
        
        uint32_t start = DWT->CYCCNT;
        run_benchmark();
        uint32_t cycles = DWT->CYCCNT - start;
        
        float time_ms = cycles / (freqs[i] * 1000.0f);
        float power_mw = measure_power_consumption();
        
        printf("Freq: %luMHz, Time: %.2fms, Power: %.1fmW, "
               "Efficiency: %.2f ops/mJ\r\n",
               freqs[i], time_ms, power_mw, 
               benchmark_ops / (power_mw * time_ms / 1000));
    }
}

七、工具链对比与选型建议

工具/方法 精度 侵入性 实时性 适用场景 推荐度
DWT CYCCNT ~6ns 极低 函数级热点分析 ⭐⭐⭐⭐⭐
ITM/SWO ~6ns 实时流输出、火焰图 ⭐⭐⭐⭐⭐
ETM Trace ~1ns 指令级跟踪(需专用硬件) ⭐⭐⭐⭐
GPIO翻转 ~10ns 极低 外部逻辑分析仪验证 ⭐⭐⭐⭐
软件插桩 ~100ns 复杂调用链分析 ⭐⭐⭐
Keil Performance Analyzer ~6ns IDE集成、可视化 ⭐⭐⭐⭐
Segger SystemView ~1μs RTOS任务级分析 ⭐⭐⭐⭐

八、总结与最佳实践

8.1 嵌入式 Profiling 核心原则

  1. 先测量,后优化:没有数据支撑的优化是"玄学",DWT 测量是第一步
  2. 关注热点:80% 的执行时间集中在 20% 的代码中,优先优化高频调用函数
  3. 验证收益:每次优化后重新测量,确认真实收益(编译器优化可能"帮倒忙")
  4. 生产环境可移除:所有 Profiling 代码通过宏控制,发布版完全零开销

8.2 推荐工作流

开发阶段 → DWT打点 + UART日志(快速迭代)
    ↓
调试阶段 → ITM/SWO实时流 + 火焰图(深度分析)
    ↓
验证阶段 → GPIO翻转 + 逻辑分析仪(硬件验证)
    ↓
发布阶段 → 关闭所有Profiling宏(零开销)

8.3 常见陷阱

陷阱 现象 解决方案
计数器溢出 长时测量出现负值 定期重置或使用 64 位累加器
中断干扰 测量值波动大 关键段用 taskENTER_CRITICAL() 保护
编译器内联 函数"消失"无法测量 __attribute__((noinline))
调试器断开 DWT 自动禁用 生产环境验证 TRCENA 持久化
低功耗模式 Sleep 时计数器停止 结合 RTC 实现跨睡眠周期测量

通过本文介绍的 DWT 周期计数器、热点分析框架与 ITM/SWO 实时流输出技术,嵌入式开发者可以在裸机/RTOS 环境中实现与 PC 端 gprof 同等甚至更优的性能分析能力。从"凭感觉优化"到"用数据说话",这是嵌入式软件工程化的重要里程碑。


转载自:https://blog.csdn.net/u014727709/article/details/162603621
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐