欢迎加入【开源鸿蒙PC社区】,一起共建鸿蒙化C/C++三方库生态。

欢迎在【PC社区】平台贡献你的项目。

资源 地址
上游仓库地址 https://github.com/microsoft/mimalloc
适配源码地址 https://atomgit.com/unisources/mimalloc
AtomCode 文档 https://atomcode.atomgit.com
lycium 交叉编译工具链 https://atomgit.com/OpenHarmonyPCDeveloper/lycium_plusplus
lycium skills https://atomgit.com/unisources/lycium_plusplus-skills
集成示例源码 https://atomgit.com/unisources/OHOSMimallocSample

image-20260628134557025

前置说明

项目 说明
适配库 mimalloc v3.3.2
目标平台 鸿蒙PC
SDK 版本 API 20 (6.0) — OHOS SDK
构建系统 CMake
依赖 零外部依赖
许可证 MIT
测试设备 鸿蒙PC 模拟器 (arm64)
核心工具 lycium_plusplus + AtomCode Skills

你将从本文获得什么

  • ✅ 一份可直接使用的 HPKBUILD 交叉编译脚本
  • ✅ 一套 13 项回归测试的 NAPI 桥接代码(分配/释放/对齐/堆/统计全覆盖)
  • ✅ 一个 ArkUI 3 列卡片网格测试页(每项用例独立展示状态+说明)
  • 3 个踩坑实录(避开 90% 的适配陷阱)
  • ✅ 一个可直接 Build 的 Git 仓库模板

核心步骤

Step 1:用 AtomCode 生成 HPKBUILD 骨架

传统方式:手写 HPKBUILD,翻文档记变量名,耗时约 20 分钟。

AtomCode Skills 方式:一条命令生成完整骨架。

/new-package mimalloc v3.3.2 https://github.com/microsoft/mimalloc/archive/refs/tags/v3.3.2.tar.gz

输出 /home/lycium_plusplus/thirdparty/mimalloc/HPKBUILD,包含:

  • 标准 Apache 2.0 头 + Contributor 信息
  • pkgname/pkgver/pkgrel/pkgdesc 完整元数据
  • archs:armeabi-v7a + arm64-v8a + x86_64
  • CMake 构建流程($OHOS_SDK/cmake + $MAKE -C
  • 日志重定向 $buildlog + check() 占位

关键点:mimalloc CMake 配置相比其他库多两个选项:

选项 作用
-DMI_BUILD_TESTS=ON 启用测试编译(交叉编译后实机运行)
-DMI_INSTALL_TOPLEVEL=ON 头文件安装到 include/ 而非 include/mimalloc/,简化 NAPI 桥接 include 路径

Step 2:交叉编译生成产物

在 lycium_plusplus 根目录执行:

./build_local.sh mimalloc arm64-v8a

产物输出到 /home/lycium_plusplus/lycium/usr/mimalloc/arm64-v8a/

include/
  ├── mimalloc.h
  ├── mimalloc-new-delete.h
  ├── mimalloc-override.h
  └── mimalloc-stats.h
lib/
  ├── libmimalloc.a          ← 最关键的静态库
  ├── libmimalloc.so
  ├── cmake/mimalloc/
  └── pkgconfig/mimalloc.pc

Step 3:生成 NAPI App 示例工程

/new-sample mimalloc "compact general purpose allocator"

AtomCode 自动匹配 OHOSSpdlogSample(零依赖 C/C++ 库模板)并执行:

  1. 复制模板项目 → /home/hoapp/OHOSMimallocSample
  2. 替换 bundleNamecom.unisources.mimalloc
  3. 替换 abiFilters["arm64-v8a"]
  4. 替换 deviceTypes["phone", "2in1"]
  5. 部署交叉编译产物(libmimalloc.a + 4 个头文件)
  6. 重写 napi_init.cpp13 项回归测试
  7. 重写 Index.ets3 列 Grid 卡片 UI

架构全景图:

HPKBUILD

arm64-v8a

部署到 thirdparty/

CMakeLists.txt 链接

NAPI 桥接

13 项回归测试

ArkUI Grid

mimalloc 源码

lycium_plusplus 交叉编译

libmimalloc.a + 头文件

OHOSMimallocSample

libentry.so

napi_init.cpp

JSON 结果

3 列卡片网格

Step 4:CMakeLists.txt — 链接静态库

cmake_minimum_required(VERSION 3.5.0)
project(OHOSMimallocSample)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

if(DEFINED PACKAGE_FIND_FILE)
    include(${PACKAGE_FIND_FILE})
endif()

# ── Add third-party header path ──
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include
                    ${NATIVERENDER_ROOT_PATH}/thirdparty/mimalloc/include)

# ── Add library search path (BEFORE add_library) ──
link_directories(${NATIVERENDER_ROOT_PATH}/thirdparty/mimalloc/lib)

# ── C++17 required by mimalloc ──
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(entry SHARED napi_init.cpp)

# ── Link libraries (system first, third-party second) ──
target_link_libraries(entry PUBLIC libace_napi.z.so)
target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/thirdparty/mimalloc/lib/libmimalloc.a)
# pthread needed by mimalloc on OHOS
target_link_libraries(entry PUBLIC pthread)

关键点

  1. include_directories 指向 thirdparty/mimalloc/include — 得益于 MI_INSTALL_TOPLEVEL=ON,头文件就在 include/ 根目录
  2. link_directories 必须在 add_library 之前,否则 CMake 无法解析库搜索路径
  3. 系统库在前、三方库在后libace_napi.z.so 先链接,libmimalloc.a 后链接(链接器从左到右解析符号)
  4. 额外链接 pthread:mimalloc 内部使用线程局部存储

Step 5:NAPI 桥接 — 13 项回归测试

napi_init.cpp 核心逻辑:调用 mimalloc API 执行测试,输出 JSON 数组供 ArkUI 渲染。

// ── 返回 JSON 数组 ──
static napi_value MimallocFullTest(napi_env env, napi_callback_info info)
{
    std::ostringstream oss;

    // 测试 1:版本信息
    {
        int ver = mi_version();
        char buf[32];
        snprintf(buf, sizeof(buf), "%d.%d.%d",
                 ver / 10000, (ver / 100) % 100, ver % 100);
        AppendJsonResult(oss, "mimalloc_version", (ver > 0),
            std::string("v") + buf);
    }

    // 测试 2:基本分配/释放
    {
        void *p = mi_malloc(128);
        if (p) { std::memset(p, 0xAB, 128); mi_free(p); }
        AppendJsonResult(oss, "malloc_free_128", (p != nullptr),
            "alloc + memset + free ok");
    }

    // ... 共 13 项测试(calloc / realloc / zalloc /
    //    aligned_alloc / malloc_small / 批量1000次 /
    //    good_size / usable_size / option / stats / heap)

    std::string json = "[" + oss.str() + "]";
    return GetNAPIFromString(env, json);
}

TypeScript 声明同样简洁:

// entry/src/main/cpp/types/libentry/Index.d.ts
export const add: (a: number, b: number) => number;
export const mimallocFullTest: () => string;

Step 6:ArkUI — 3 列 Grid 卡片网格

// entry/src/main/ets/pages/Index.ets
@Entry
@Component
struct Index {
  @State testResults: TestResult[] = [];
  @State hasRun: boolean = false;
  @State allPassed: boolean = false;

  build() {
    Column() {
      // 运行按钮 + 汇总状态条 + Grid 网格
      Grid() {
        ForEach(this.testResults, (item: TestResult) => {
          GridItem() { this.TestCard(item) }
        })
      }
      .columnsTemplate('1fr 1fr 1fr')
      .columnsGap(8)
      .rowsGap(8)
      .width('100%')
      .layoutWeight(1)
    }
  }

  @Builder TestCard(item: TestResult) {
    Column() {
      Row() {
        Text(item.p ? '✓' : '✗').fontColor(item.p ? '#10B981' : '#EF4444')
        Text(this.toDisplayName(item.n)).fontSize(12)
      }
      Text(item.c).fontSize(10).fontColor('#64748B')  // 中文说明
      Text(item.d).fontSize(9).fontColor(item.p ? '#10B981' : '#EF4444')
    }
    .backgroundColor(item.p ? '#D1FAE5' : '#FEE2E2')
    .borderRadius(8)
    .height(78)
  }
}

界面效果:

┌─────────────────────────────────────┐
│  [ 运行全部 13 项测试          → ]  │
│  NAPI 基线: 2 + 3 = 5               │
│  ✓ 全部通过  13 / 13 项             │
├─────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌───────┐│
│ │✓ 版本信息 │ │✓ 分配释放│ │✓ 零初 ││
│ │验证mi_ver │ │测试mi_mal│ │测试mi_││
│ │v3.3.2    │ │alloc+…   │ │16x64… ││
│ └──────────┘ └──────────┘ └───────┘│
│ ┌──────────┐ ┌──────────┐ ┌───────┐│
│ │✓ 重分配  │ │✓ 对齐分配│ │✓ 小块 ││
│ │...       │ │...       │ │...    ││
│ └──────────┘ └──────────┘ └───────┘│
└─────────────────────────────────────┘

每张卡片 3 层信息:状态 + 测试名 → 中文说明 → 结果详情。通过底色(绿色通过 / 红色失败)一目了然。


踩坑专区

问题 1:mi_heap_free API 不存在

现象

error: use of undeclared identifier 'mi_heap_free'
                mi_heap_free(heap, p);
                       ^

根因:mimalloc 的内部 heap API 设计和其他内存分配器不同——它没有 mi_heap_free() 函数。Heap 分配的块使用普通 mi_free() 释放,由 mimalloc 内部通过指针地址查找所属 heap。

排查过程:最初我看到 mimalloc 文档里有 mi_heap_new()mi_heap_malloc(),直觉认为应该有 mi_heap_free()。搜索 <mimalloc.h> 全部 mi_heap_* 函数后发现没有这个 API。

修复方案

- if (ok) {
-     mi_heap_free(heap, p);
- }
  mi_heap_destroy(heap);   // destroy 自动释放所有存活块

经验总结mi_heap_destroy() 释放堆内所有存活块,无需预先释放。这也是 mimalloc 的安全设计——避免手动释放时传递错误 heap 指针。

问题 2:mi_stats_merge 符号缺失

现象

ld.lld: error: undefined symbol: mi_stats_merge
>>> referenced by napi_init.cpp:256

根因mi_stats_merge 是 mimalloc 的内部调试函数,在这个交叉编译构建版本中没有导出。实际导出的只有 _mi_stats_merge_into(带下划线的内部符号)。这是交叉编译时 MI_DEBUG=0(Release 模式)的默认行为。

修复方案

- void *p = mi_malloc(4096);
- mi_free(p);
- mi_stats_merge();       // 未导出
  ok = ok && (stats.version > 0);   // 直接验证结构体即可

经验总结:mimalloc 的统计 API 遵循"头文件声明 ≠ 真实导出"的规律。交叉编译时统计相关函数默认被裁剪。最佳实践:nm 检查 .a 文件中的实际符号,不依赖头文件声明。

nm libmimalloc.a | grep mi_stats
# 只看到 mi_stats_get, _mi_stats_merge_into — 没有 mi_stats_merge

问题 3:mi_stats_get 填充失败

现象

[FAIL] stats_get -> stats retrieval failed

根因mi_stats_t 结构体以 size 字段开头作为 API 版本兼容性校验。mi_stats_get() 内部检查 stats->size == sizeof(mi_stats_t),不匹配则返回 false。初始代码用 memset 清零导致 size=0

修复方案

  mi_stats_t stats;
- std::memset(&stats, 0, sizeof(stats));
+ mi_stats_init(&stats);           // 初始化 size = sizeof(mi_stats_t)
  bool ok = mi_stats_get(&stats);

经验总结:查看 mimalloc-stats.h 发现 mi_stats_header_init() 内部设置 stats->size = sizeof(*stats)stats->version = MI_STAT_VERSION。调用 mi_stats_get() 前必先初始化 header,这是 API 版本兼容性的标准模式(类似 Windows API 的 cbSize 或 Linux 的 struct statx)。


通用模板(拿来即用)

完整 HPKBUILD

# lycium_plusplus/thirdparty/mimalloc/HPKBUILD

pkgname=mimalloc
pkgver=v3.3.2
pkgrel=0
pkgdesc="mimalloc is a compact general purpose allocator with excellent performance characteristics"
url="https://github.com/microsoft/mimalloc"
archs=("armeabi-v7a" "arm64-v8a" "x86_64")
license=("MIT")
depends=()
makedepends=()

source="https://github.com/microsoft/mimalloc/archive/refs/tags/${pkgver}.tar.gz"

autounpack=true
downloadpackage=true
patchflag=false
buildtools="cmake"

builddir=$pkgname-${pkgver:1}
packagename=$builddir.tar.gz

prepare() {
    mkdir -p $builddir/$ARCH-build
}

build() {
    cd $builddir
    $OHOS_SDK/native/build-tools/cmake/bin/cmake "$@" \
        -B$ARCH-build -S./ \
        -DMI_BUILD_TESTS=ON \
        -DMI_INSTALL_TOPLEVEL=ON \
        > $buildlog 2>&1
    $MAKE -C $ARCH-build VERBOSE=1 >> $buildlog 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

package() {
    cd $builddir
    $MAKE -C $ARCH-build install >> $buildlog 2>&1
    ret=$?
    cd $OLDPWD
    return $ret
}

check() {
    echo "The test must be on an OpenHarmony device!"
}

cleanbuild() {
    rm -rf ${PWD}/$builddir
}

NAPI 桥接核心代码(JSON 输出模式)

// ── JSON 条目构建器(轻量版,无第三方 JSON 库依赖)──
static void AppendJsonResult(std::ostringstream &oss,
                             const char *testName, bool passed,
                             const std::string &detail = "",
                             const std::string &desc = "")
{
    if (oss.tellp() > 0) oss << ",";
    auto escape = [&](const std::string &s) {
        for (char c : s) {
            if (c == '"' || c == '\\') oss << '\\';
            oss << c;
        }
    };
    oss << "{\"n\":\"";  escape(testName);
    oss << "\",\"p\":" << (passed ? "true" : "false");
    oss << ",\"d\":\"";  escape(detail);
    oss << "\",\"c\":\""; escape(desc);
    oss << "\"}";
}

// ── NAPI 模块注册 ──
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "mimallocFullTest", nullptr, MimallocFullTest, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}

延伸思考

内存分配器移植对比

对比维度 mimalloc jemalloc tcmalloc 系统 malloc
构建系统 CMake autotools + CMake Bazel 内置
外部依赖 零依赖 依赖 pthread 依赖 libc++ 内核
鸿蒙适配难度 ★☆☆ 低 ★★★ 中 ★★★★ 高
交叉编译注意事项 无特殊 需禁用 je_malloc_conf 需处理 Bazel 规则
API 复杂度 简单(30+ 函数) 中等(60+ 函数) 中等 简单
性能特性 紧凑、快分配 大内存友好 多线程优化 通用

mimalloc 是当前最适合鸿蒙 PC 适配的内存分配器,原因:

  1. 零外部依赖 — 只需 CMake 即可编译,无需处理复杂的依赖链
  2. MI_INSTALL_TOPLEVEL — 头文件安装扁平化,简化 include 路径
  3. MIT 许可证 — 商业友好

通用方法论:三步速通任何 C/C++ 库的鸿蒙适配

  1. HPKBUILD 生成new-package skill → 自动适配 CMake/configure/Makefile
  2. 交叉编译build_local.sh <name> arm64-v8a → 输出 .a + include/
  3. NAPI 示例生成new-sample skill → 自动桥接 + ArkUI 测试页

总结

mimalloc 是微软开源的紧凑型通用内存分配器,零外部依赖、MIT 许可证,堪称鸿蒙 PC 三方库移植的"入门级"佳作。通过 AtomCode Skills 的 new-package + new-sample 两条命令,从零开始仅需 30 分钟即可完成:

  1. ✅ HPKBUILD 交叉编译脚本
  2. ✅ arm64-v8a 静态库产物
  3. ✅ 13 项回归测试的 NAPI 桥接
  4. ✅ 3 列 Grid 卡片 ArkUI 验证页面
  5. ✅ 3 个踩坑实录的修复记录

适配的核心不是编译本身,而是理解目标库的 API 设计哲学(如 mimalloc 没有 mi_heap_free)、产物的符号裁剪规则(如 Release 模式不导出调试函数)、以及结构体初始化约定(如 mi_stats_t 的 size 版本校验)。

关键数据:传统方式适配一个三方库需要约 4-8 小时(手写 HPKBUILD + 手写 NAPI + 调试),使用 AtomCode Skills 可缩短至 30-50 分钟,效率提升约 10 倍

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐