3FS USRBIO静态库编译与性能实战指南

系列文章快速导航


引言:探索3FS的USRBIO性能潜力

近期,3FS 依然是技术圈内备受关注的焦点。继上一篇我们对 3FS 元数据性能进行了详尽的测试与分析之后,我们决定将目光投向 3FS 的另一项核心技术创新:FUSE 与 USRBIO 的并行使用。通过实际的编译与性能体验,我们发现 3FS 在设计上巧妙地平衡了用户空间文件系统 FUSE (Filesystem in Userspace) 的易用性与原生接口 USRBIO 的极致性能。

具体来说:

  • 在大多数应用场景下,用户可以直接通过 FUSE 挂载 3FS,几乎无需对现有 AI 任务代码进行任何修改,即可平滑迁移。这种便捷性极大地降低了上手门槛。
  • 而对于追求极限性能的场景,3FS 提供了 USRBIO 接口。该接口支持零拷贝 (zero-copy) 和**批量异步提交 (batch asynchronous submission)**机制,能够显著减少不必要的系统调用和网络交互开销。这对于需要处理海量数据、对延迟敏感的大规模深度学习集群而言,无疑是一个极具吸引力的设计。

值得一提的是,3FS 通过形式化验证 (formal verification) 的手段,确保了系统在遭遇各种潜在故障时,依然能够保持数据的一致性高可用性。而 USRBIO 接口凭借其零拷贝和异步批量 I/O 的特性,在处理大规模并行计算和低延迟需求时,展现出了尤为突出的优势。

本文作为九章云极 3FS 系列分享的第三篇,旨在通过一个完整的操作实例,带领大家一步步完成 3FS USRBIO 静态库的编译,并初步体验其性能表现。主要步骤概览如下 (点击标题可快速跳转至相应章节):

准备工作:环境与路径

本次编译操作我们选用的环境是 Ubuntu 22.04 LTS,这也是 3FS 官方明确支持的操作系统版本。

您可以通过以下命令来确认当前系统的版本信息:

$ cat /etc/issue
Ubuntu 22.04.5 LTS \n \l

为方便后续操作,我们将默认的编译工作路径设置在当前用户的 Home 目录下:

$ export BUILD_DIR=$HOME

安装编译依赖

要成功静态编译 USRBIO,我们需要确保系统中已安装 gcc-12g++-12clang-14 等特定版本的编译工具,以及一系列第三方库。请执行以下命令进行安装:

$ sudo apt update
$ sudo apt -y install cmake libuv1-dev liblz4-dev liblzma-dev libdouble-conversion-dev libdwarf-dev libunwind-dev libaio-dev libgflags-dev libgoogle-glog-dev libgtest-dev libgmock-dev libgoogle-perftools-dev google-perftools libssl-dev gcc-12 g++-12 libboost-all-dev build-essential git

重要提示:Ubuntu 系统默认的 sh 命令链接指向的是 dash。为了避免编译过程中可能出现的兼容性问题,我们需要将其切换为 bash

sudo ln -sf /usr/bin/bash /usr/bin/sh

编译3FS USRBIO静态库

接下来,我们将正式开始编译 3FS 的 USRBIO 静态库。

1. 下载3FS源码并应用补丁

首先,从 GitHub 克隆 3FS 的官方仓库,并应用项目提供的补丁:

$ cd ${BUILD_DIR}
$ git clone https://github.com/deepseek-ai/3fs
$ cd 3fs
$ git submodule update --init --recursive # 初始化并拉取子模块
$ ./patches/apply.sh # 应用补丁

2. 编译预处理

由于我们的目标仅仅是编译 usrbio 的静态库,而非整个 3FS 工程,因此可以对项目根目录下的 CMakeLists.txt 文件进行适当修改,以精简编译过程,减少不必要的依赖项和编译时间。建议注释掉以下几行:

#include(cmake/CodeCoverage.cmake)
#include(cmake/CLangFormat.cmake)
#include(cmake/CLangTidy.cmake)
#include(cmake/DumpConfig.cmake)
#include(cmake/ApacheArrow.cmake)
#add_subdirectory(tests)
#add_subdirectory(benchmarks)

此外,为了使生成的 libusrbio.a 静态库能够包含其依赖的 3FS 基础库(如 libcommon.alibcore-user-fbs.alibclient-lib-common.a),从而简化后续的集成链接过程,我们需要对 src/lib/api/CMakeLists.txt 文件进行修改,添加如下内容:

add_library(usrbio STATIC
$<TARGET_OBJECTS:hf3fs_api>
$<TARGET_OBJECTS:client-lib-common>
$<TARGET_OBJECTS:core-user-fbs>
$<TARGET_OBJECTS:common>)

这样做的好处是,最终得到的 libusrbio.a 将是一个更为“自包含”的库,方便开发者在自己的项目中使用。

3. 执行编译

完成预处理后,就可以开始编译了:

$ cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=${BUILD_DIR}/3fs/build/lib
$ cmake --build build --target usrbio -j # -j 参数利用多核并行编译

如果一切顺利,编译过程的最后阶段,您应该能看到类似如下的输出信息:

[100%] Linking CXX static library libhf3fs_api.a
[100%] Built target hf3fs_api
[100%] Linking CXX static library libusrbio.a
[100%] Built target usrbio

编译完成后,我们所需要的静态库文件(.a 文件)将会生成在 ${BUILD_DIR}/3fs/build/lib 目录下。您可以检查一下:

$ ls -ltr ${BUILD_DIR}/3fs/build/lib/ # 注意原文档为libs,根据上下文推断应为lib
total 443180
-rw-rw-r--. 1 dingofs dingofs 48798 Apr 16 04:30 libcityhash-lib.a
-rw-rw-r--. 1 dingofs dingofs 171914 Apr 16 04:30 libabsl-lib.a
-rw-rw-r--. 1 dingofs dingofs 175306 Apr 16 04:30 lib3fs_liburing.a
-rw-rw-r--. 1 dingofs dingofs 27776 Apr 16 04:30 libversion-info.a
-rw-rw-r--. 1 dingofs dingofs 1204216 Apr 16 04:31 libfmt.a
-rw-rw-r--. 1 dingofs dingofs 688614 Apr 16 04:31 liblz4-lib.a
-rw-rw-r--. 1 dingofs dingofs 5429158 Apr 16 04:31 libzstd.a
-rw-rw-r--. 1 dingofs dingofs 9366042 Apr 16 04:31 libclickhouse-cpp-lib-static.a
-rw-rw-r--. 1 dingofs dingofs 5510568 Apr 16 04:31 libscn.a
-rw-rw-r--. 1 dingofs dingofs 112968332 Apr 16 04:31 libfolly.a
-rw-rw-r--. 1 dingofs dingofs 1385464 Apr 16 04:31 libmemory-common.a
-rw-rw-r--. 1 dingofs dingofs 8 Apr 16 04:31 libMonitorCollectorService-fbs.a
-rw-rw-r--. 1 dingofs dingofs 104703994 Apr 16 04:32 libcommon.a
-rw-rw-r--. 1 dingofs dingofs 8 Apr 16 04:32 libcore-service-fbs.a
-rw-rw-r--. 1 dingofs dingofs 550646 Apr 16 04:32 libcore-user-fbs.a
-rw-rw-r--. 1 dingofs dingofs 3075252 Apr 16 04:32 libmgmtd-fbs.a
-rw-rw-r--. 1 dingofs dingofs 1393496 Apr 16 04:33 libclient-lib-common.a
-rw-rw-r--. 1 dingofs dingofs 16181962 Apr 16 04:33 libmgmtd-stub.a
-rw-rw-r--. 1 dingofs dingofs 8 Apr 16 04:33 libstubs-common.a
-rw-rw-r--. 1 dingofs dingofs 49175366 Apr 16 04:35 libstorage-client.a
-rw-rw-r--. 1 dingofs dingofs 14345214 Apr 16 04:36 libmgmtd-client.a
-rw-rw-r--. 1 dingofs dingofs 417556 Apr 16 04:36 libstorage-fbs.a
-rw-rw-r--. 1 dingofs dingofs 9779966 Apr 16 04:37 libmeta-fbs.a
-rw-rw-r--. 1 dingofs dingofs 1902524 Apr 16 04:37 libhf3fs_api.a
-rw-rw-r--. 1 dingofs dingofs 115265020 Apr 16 04:37 libusrbio.a # 目标静态库

3FS USRBIO 依赖于一些第三方库,如 fmtscnfolly。这些库在之前的编译步骤中已经一同编译生成。为了方便使用,我们将这些依赖库以及 libusrbio.a 统一复制到一个指定目录(例如 /opt/usrbio):

$ sudo mkdir -p /opt/usrbio
$ sudo cp ${BUILD_DIR}/3fs/build/lib/libfmt.a ${BUILD_DIR}/3fs/build/lib/libscn.a ${BUILD_DIR}/3fs/build/lib/libfolly.a ${BUILD_DIR}/3fs/build/lib/libusrbio.a /opt/usrbio/

同时,不要忘记将 USRBIO 的头文件 hf3fs_usrbio.h 也复制到该目录下:

$ sudo cp ${BUILD_DIR}/3fs/src/lib/api/hf3fs_usrbio.h /opt/usrbio/

体验USRBIO静态库

在使用 USRBIO 之前,请确保您已经正确安装并部署了 3FS 文件系统,并且目标文件系统已经成功挂载。
关于 3FS 的安装部署流程,您可以参考我们早前发布的系列文章:

1. 编写测试代码

我们来编写一个简单的 C++ 程序 (main.cc),用以测试 USRBIO 的基本读取功能:

// main.cc
#include <hf3fs_usrbio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

constexpr uint64_t NUM_IOS = 1024; // 定义I/O操作的数量
constexpr uint64_t BLOCK_SIZE = (32 << 20); // 定义每个I/O操作的块大小 (32MB)

int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <mountpoint> <filepath>\n", argv[0]);
return 1;
}

char* mountpoint = argv[1]; // 3FS挂载点路径
char* filename = argv[2]; // 待读取的文件路径(在3FS挂载点内)

// 初始化I/O请求环
struct hf3fs_ior ior;
hf3fs_iorcreate4(&ior, mountpoint, NUM_IOS, true, 0, 0, -1, 0);

// 创建I/O向量缓冲区
struct hf3fs_iov iov;
hf3fs_iovcreate(&iov, mountpoint, NUM_IOS * BLOCK_SIZE, 0, -1);

// 打开文件并注册文件描述符
int fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("Failed to open file");
// 清理资源
hf3fs_iovdestroy(&iov);
hf3fs_iordestroy(&ior);
return 1;
}
hf3fs_reg_fd(fd, 0);

// 准备I/O操作
for (uint64_t i = 0; i < NUM_IOS; i++) {
hf3fs_prep_io(&ior, &iov, true, iov.base + i * BLOCK_SIZE, fd, i * BLOCK_SIZE, BLOCK_SIZE, nullptr);
}

// 提交I/O操作
hf3fs_submit_ios(&ior);

// 等待I/O操作完成
struct hf3fs_cqe cqes[NUM_IOS];
hf3fs_wait_for_ios(&ior, cqes, NUM_IOS, NUM_IOS, nullptr); // 等待所有NUM_IOS个操作完成

// 清理工作
hf3fs_dereg_fd(fd);
close(fd);
hf3fs_iovdestroy(&iov);
hf3fs_iordestroy(&ior);

printf("Successfully read file: %s using USRBIO!\n", filename);
return 0;
}

您可以将以上代码保存为 main.cc

2. 链接USRBIO静态库

使用 g++ 编译器编译 main.cc,并链接我们之前准备好的 USRBIO 静态库及其依赖:

$ g++ main.cc -o main -I/opt/usrbio/ -L/opt/usrbio -Wl,-Bstatic -lusrbio -lfolly -lfmt -lscn -Wl,-Bdynamic -lnuma -ldouble-conversion -levent -lboost_filesystem -lboost_context -lglog -lgflags -lpthread -ldl # 确保链接顺序和依赖

说明

  • -I/opt/usrbio/ 指定头文件搜索路径。
  • -L/opt/usrbio/ 指定库文件搜索路径。
  • -Wl,-Bstatic-Wl,-Bdynamic 用于控制静态链接和动态链接的优先级。我们优先静态链接 usrbio, folly, fmt, scn
  • 前面我们已经通过包管理器安装了 libnumalibdouble-conversionlibeventboost 等 USRBIO 的间接依赖库,因此在链接时可以直接引用它们。此处我们倾向于动态链接这些系统级或广泛使用的库,除非有特殊需求。

编译完成后,我们可以使用 ldd 命令检查 main 程序所依赖的动态库:

$ ldd main
linux-vdso.so.1 (0x00007ffda67ef000)
libnuma.so.1 => /lib/x86_64-linux-gnu/libnuma.so.1 (0x00007f1cf8c0e000) # 示例,实际输出可能不同
libdouble-conversion.so.3 => /lib/x86_64-linux-gnu/libdouble-conversion.so.3 (0x00007f1cf8be4000) # 示例
libevent-2.1.so.7 => /lib/x86_64-linux-gnu/libevent-2.1.so.7 (0x00007f1cf8b8a000) # 示例
libboost_filesystem.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_filesystem.so.1.74.0 (0x00007f1cf8b6a000) # 示例
libboost_context.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_context.so.1.74.0 (0x00007f1cf8b62000) # 示例
libglog.so.0 => /lib/x86_64-linux-gnu/libglog.so.0 (0x00007f1cf8b1c000) # 示例
libgflags.so.2.2 => /lib/x86_64-linux-gnu/libgflags.so.2.2 (0x00007f1cf8af5000) # 示例
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1cf882b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f1cf8744000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1cf8724000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1cf84fb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1cf8ccb000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1cf84f6000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1cf84f0000)

ldd 的输出可以看出,main 程序不再直接依赖 libusrbio.alibfolly.a 等我们期望静态链接的库,说明它们已成功编译进可执行文件中。

3. 验证测试程序

现在,我们将编译好的 main 程序复制到一台已部署并挂载了 3FS 集群的测试环境中进行验证。假设测试环境的 IP 地址为 10.220.32.17,用户为 dingofs

$ scp main dingofs@10.220.32.17:/home/dingofs/

在测试环境中,假设 3FS 的挂载点为 /mnt/3fs。我们需要在该挂载点下创建一个用于测试的大文件,例如 example.bin
首先确认挂载情况:

$ df -h /mnt/3fs
Filesystem Size Used Avail Use% Mounted on
hf3fs.zetyun 33T 506G 33T 2% /mnt/3fs

查看挂载点下的文件,并假设已有一个名为 example.bin 的测试文件(例如1GB大小):

$ ls -lhtr /mnt/3fs
total 1.1G
dr-xr-xr-x 1 root root 0 Jun 1 2023 3fs-virt
drwxr-xr-x 1 root root 0 Apr 9 10:43 10ef7f0d-86d5-4261-8e95-baed52056878
-rw-r--r-- 1 root root 1.0G Apr 14 18:30 example.bin # 假设这是一个1GB的文件

如果文件不存在,您可以使用 dd 或其他工具创建一个大文件,例如:
sudo dd if=/dev/zero of=/mnt/3fs/example.bin bs=1M count=1024

现在,运行我们的测试程序(可能需要 sudo 权限,具体取决于 3FS 的配置和 USRBIO 的权限要求):

$ sudo ./main /mnt/3fs /mnt/3fs/example.bin
Successfully read file: /mnt/3fs/example.bin using USRBIO!

看到这个输出,就表明我们的 USRBIO 测试程序已经成功运行。接下来,您可以进一步体验 USRBIO 带来的强大 I/O 读取性能了。

性能表现初探

我们对不同大小的文件进行了顺序读取测试,以下是初步的耗时数据,供您参考:

文件大小 读取耗时
1GB 1.645s
4GB 1.844s
8GB 1.874s
16GB 3.069s
32GB 4.243s

从实测结果来看,USRBIO 静态库在处理大文件顺序读取时,展现出了相当优异的性能特性。随着文件体积的增大,其高效的 I/O 处理能力愈发明显。

请注意:本评估结果严格基于实际测试数据,未包含任何推测性的技术指标。测试环境、硬件配置等因素均可能影响最终性能数据。

结语与展望

本次关于 3FS USRBIO 静态库编译与初步性能体验的分享到此告一段落。如果您在阅读过程中发现任何描述不够清晰或存在疏漏之处,欢迎随时关注“九章云极”公众号与我们取得联系并深入探讨。

我们非常期待在评论区看到您:

  • 对 USRBIO 的实践心得与思考;
  • 在类似场景下进行性能优化的宝贵经验;
  • 在测试 USRBIO 过程中遇到的挑战与解决方案;
  • 对本文测试方法的可优化建议;
  • 或者您最希望我们接下来验证的性能场景。

|关于我们

本次实操教程由九章云极的研发团队成员为您呈现。

北京九章云极科技有限公司(简称“九章云极”)成立于2013年,是一家专注于推动人工智能基础软件规模化应用的高新技术企业。我们致力于融合世界前沿的人工智能技术,通过自主创新的“算力包”产品和智算操作系统,为广大用户提供“算力+算法”一体化的人工智能服务。

|文末彩蛋:DingoFS 分布式存储系统

最后,向大家介绍一款具有更高通用性和成本效益的存储解决方案——DataCanvas DingoFS 分布式存储系统。该系统由北京九章云极科技有限公司自主研发,于2024年11月20日首次发布,并于2025年1月14日完成软件著作权登记。DingoFS 凭借其高效的数据存储与管理能力、对大规模数据的分布式存储支持、以及出色的高可用性和可扩展性,在业界获得了广泛关注,尤其适用于需要处理海量数据并对系统可靠性有严苛要求的应用场景。DingoFS 即将发布的新版本,将在元数据性能方面实现进一步的飞跃。

DingoFS 核心特性一览:

  • POSIX 兼容性:提供与本地文件系统几乎一致的操作体验,确保与现有应用和系统的无缝集成。
  • AI 原生架构:针对大语言模型(LLM)等 AI 工作流进行了深度优化,能够高效管理海量的训练数据集和模型检查点(Checkpoint)工作负载。
  • S3 协议兼容:支持标准的 Amazon S3 接口协议,方便用户通过 S3 工具便捷地访问文件系统命名空间。
  • 全分布式架构:元数据服务(MDS)、数据存储层、缓存系统以及客户端组件均支持在线线性扩展,满足业务增长需求。
  • 卓越性能表现:兼具本地 SSD 级别的低延迟响应与对象存储级别的弹性吞吐能力,应对多样化I/O负载。
  • 智能缓存加速体系:构建了内存、本地 SSD、分布式集群三级缓存拓扑结构,为 AI 应用场景提供高吞吐、低时延的智能化 I/O 加速。

|Alaya NeW算力云:让DeepSeek部署更简单!

借助 Alaya NeW算力云服务 提供的强大 GPU 资源,您可以轻松实现在云端部署 DeepSeek 等大模型的推理服务,并根据实际需求灵活调配算力,为您的技术创新与科研探索提供坚实的高效能计算支持!

三步搞定一键部署,快速上手DeepSeek!

不想被繁琐的配置流程所困扰?别担心!只需简单三步,您就能轻松完成 DeepSeek 大语言模型的一键部署。立即行动起来,体验前沿AI的魅力吧!

点击此处,免费体验25元算力包,一键部署DeepSeek!