[GH-ISSUE #398] 读取1MBx16和512KBx32的速度差异问题 #232

Closed
opened 2026-10-26 09:47:31 +00:00 by bsakaguchi · 10 comments
Owner

Originally created by @lcxywfe on GitHub (Feb 23, 2026).
Original GitHub issue: https://github.com/deepseek-ai/3FS/issues/398

测试环境

1台meta,3台storage,每台storage有16块ssd,1台client。

测试文件

/3fs/test.*.*是fio生成的大小为2GB的文件

测试代码

def test(iov, ior, iters, batch, block, pidx = 0):
    fds = []
    offsets = []
    for i in range(iters):
        offs = []
        for j in range(batch):
            offs.append(random.randint(1, 1024 * 1024 * 1024))
        offsets.append(offs)
    for iter in range(iters):
        ios = []
        fds = []
        for i in range(batch):
            path = f"/3fs/test.{pidx % 128}.{i % 10}"
            fd = os.open(path, os.O_RDONLY)
            register_fd(fd)
            fds.append(fd)
            ios.append([iov[i * block * 1024: (i + 1) * block * 1024], fd, offsets[iter][i]])

        for io in ios:
            ior.prepere(io[0], True, io[1], io[2])
        ior.submit().wait(min_result=len(ios))

        for fd in fds:
            deregister_fd(fd)
            os.close(fd)

def run():
    def worker(pidx):
        iters = 256
        batch = 32
        block = 512
        shm = SharedMemory(size = batch * block * 1024, create=True)
        iov = make_iovec(shm, "/3fs", 0, -1)
        ior = make_ioring("/3fs", batch, True, 0)
        shm.unlink()
        test(iov, ior, iters, batch, block, pidx)
    num = 128
    ps = []
    for i in range(num):
        p = mp.Process(target=worker, args=(i,))
        p.start()
        ps.append(p)
    for p in ps:
        p.join()

同时从从batch个不同的文件读取block大小。
当batch和block分别为16和1024时,读取速度达到45GB/s。
当batch和block分别为32和512时,读取速度只有22GB/s。
将max_idle_threads从10改成32之后,速度上升至30GB/s。
meta节点的latency都是3ms左右。
此外我还尝试通过调整offset,让读1MB时正好跨两个chunk,512KB不跨chunk,这样实际访问的chunk数量和大小都是一致的,但速度没有明显区别。
请问这个问题该如何排查。

另外修改src\fuse\PioV.cc中的executeRead

CoTryTask<void> PioV::executeRead(const UserInfo &userInfo, const storage::client::ReadOptions &options) {
  assert(wios_.empty() && trops_.empty());

  *co_return Void{};

  if (rios_.empty()) {
    co_return Void{};
  }

  co_return co_await storageClient_.batchRead(rios_, userInfo, options);
}

不执行实际读取,直接返回,ib的流量确实是空的,但是整体的耗时反而增加了。

Originally created by @lcxywfe on GitHub (Feb 23, 2026). Original GitHub issue: https://github.com/deepseek-ai/3FS/issues/398 ## 测试环境 1台meta,3台storage,每台storage有16块ssd,1台client。 ## 测试文件 `/3fs/test.*.*`是fio生成的大小为2GB的文件 ## 测试代码 ``` def test(iov, ior, iters, batch, block, pidx = 0): fds = [] offsets = [] for i in range(iters): offs = [] for j in range(batch): offs.append(random.randint(1, 1024 * 1024 * 1024)) offsets.append(offs) for iter in range(iters): ios = [] fds = [] for i in range(batch): path = f"/3fs/test.{pidx % 128}.{i % 10}" fd = os.open(path, os.O_RDONLY) register_fd(fd) fds.append(fd) ios.append([iov[i * block * 1024: (i + 1) * block * 1024], fd, offsets[iter][i]]) for io in ios: ior.prepere(io[0], True, io[1], io[2]) ior.submit().wait(min_result=len(ios)) for fd in fds: deregister_fd(fd) os.close(fd) def run(): def worker(pidx): iters = 256 batch = 32 block = 512 shm = SharedMemory(size = batch * block * 1024, create=True) iov = make_iovec(shm, "/3fs", 0, -1) ior = make_ioring("/3fs", batch, True, 0) shm.unlink() test(iov, ior, iters, batch, block, pidx) num = 128 ps = [] for i in range(num): p = mp.Process(target=worker, args=(i,)) p.start() ps.append(p) for p in ps: p.join() ``` 同时从从`batch`个不同的文件读取`block`大小。 当`batch`和`block`分别为16和1024时,读取速度达到45GB/s。 当`batch`和`block`分别为32和512时,读取速度只有22GB/s。 将`max_idle_threads`从10改成32之后,速度上升至30GB/s。 meta节点的latency都是3ms左右。 此外我还尝试通过调整offset,让读1MB时正好跨两个chunk,512KB不跨chunk,这样实际访问的chunk数量和大小都是一致的,但速度没有明显区别。 请问这个问题该如何排查。 另外修改`src\fuse\PioV.cc`中的`executeRead` ``` CoTryTask<void> PioV::executeRead(const UserInfo &userInfo, const storage::client::ReadOptions &options) { assert(wios_.empty() && trops_.empty()); *co_return Void{}; if (rios_.empty()) { co_return Void{}; } co_return co_await storageClient_.batchRead(rios_, userInfo, options); } ``` 不执行实际读取,直接返回,ib的流量确实是空的,但是整体的耗时反而增加了。
Author
Owner

@SF-Zhou commented on GitHub (Feb 24, 2026):

看你的描述底层 IO 应该是一致的才对。建议观察下测试过程中的 metrics,对比这两种场景下 metrics 不一致的地方,包括:

  1. storage_client.concurrent_user_calls
  2. storage_client.bytes_per_user_call
  3. storage_client.bytes_per_operation
  4. storage_client.ops_per_user_call
  5. storage_client.ops_per_request
  6. storage_client.bytes_per_request
  7. storage_client.inflight_requests
  8. storage_client.overall_latency
<!-- gh-comment-id:3949908778 --> @SF-Zhou commented on GitHub (Feb 24, 2026): 看你的描述底层 IO 应该是一致的才对。建议观察下测试过程中的 metrics,对比这两种场景下 metrics 不一致的地方,包括: 1. storage_client.concurrent_user_calls 2. storage_client.bytes_per_user_call 3. storage_client.bytes_per_operation 4. storage_client.ops_per_user_call 5. storage_client.ops_per_request 6. storage_client.bytes_per_request 7. storage_client.inflight_requests 8. storage_client.overall_latency
Author
Owner

@lcxywfe commented on GitHub (Feb 25, 2026):

Image Image Image Image Image Image Image Image Image Image Image Image

@SF-Zhou 我截了grafana的图,read bytes的图和其他的图时间上对不上正常吗?

<!-- gh-comment-id:3957225953 --> @lcxywfe commented on GitHub (Feb 25, 2026): <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/bb5415a9-2aac-49c0-a512-a036469347db" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/6699f3e2-0491-4e58-89e7-9ec6eac9daba" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/97762548-a0d3-4534-934a-272279af4fb4" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/f3853dd4-933f-4a42-9345-c7c46081cd4d" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/e06b2534-c20e-432a-b66d-030b4a90d535" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/54fb8dc7-c958-41d3-993f-95a3a8625ad1" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/9b18bf78-f5de-4eec-be16-3f29c0a6f1d7" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/699eab13-d172-4e72-91df-512560424511" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/b67250ad-1b6b-4274-8523-f3569515a9f9" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/2142ead5-300b-4903-b2c4-543dc5cb613e" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/253cc108-846c-4bd0-932c-09e25b30750d" /> <img width="3835" height="2046" alt="Image" src="https://github.com/user-attachments/assets/ca7b784d-0c10-4e6e-b4bc-59eff95f01c8" /> @SF-Zhou 我截了grafana的图,read bytes的图和其他的图时间上对不上正常吗?
Author
Owner

@SF-Zhou commented on GitHub (Feb 25, 2026):

能观察到 concurrent_user_calls 有很大的差异,其他结果应该也是并发不同导致的。我看你的代码每个 batch 里都会注册一遍文件,这样 batch 大小不同时注册的数量就不一样。你可以考虑一次性注册完,这样不对测试产生影响,然后给核心 IO 和其他操作加上 latency 记录。

<!-- gh-comment-id:3958248124 --> @SF-Zhou commented on GitHub (Feb 25, 2026): 能观察到 concurrent_user_calls 有很大的差异,其他结果应该也是并发不同导致的。我看你的代码每个 batch 里都会注册一遍文件,这样 batch 大小不同时注册的数量就不一样。你可以考虑一次性注册完,这样不对测试产生影响,然后给核心 IO 和其他操作加上 latency 记录。
Author
Owner

@lcxywfe commented on GitHub (Feb 26, 2026):

能观察到 concurrent_user_calls 有很大的差异,其他结果应该也是并发不同导致的。我看你的代码每个 batch 里都会注册一遍文件,这样 batch 大小不同时注册的数量就不一样。你可以考虑一次性注册完,这样不对测试产生影响,然后给核心 IO 和其他操作加上 latency 记录。

@SF-Zhou 感谢回复!

确实排除register_fd之后,两者的速度差距缩小了,40GB/s和38GB/s,concurrent_user_calls 基本一致。
但我有的场景确实需要每次读不同的文件,有办法通过线程数量的提升来提高并发吗(除了max_idle_threads之外)。

此外如果block进一步降低,变成256KB x 64,concurrent_user_calls依然和之前一致,但速度会明显下降(23GB/s).
观察到bytes_per_user_call和bytes_per_user_request在block降低时同比例降低。
但是ops_per_user_call和ops_per_request在block从1MB降低到512KB时是同比例降低的,从512KB降低到256KB时几乎没有降低。
是否说明block的太小会导致访问太碎,ops上升,请问这个问题有办法解决吗?比如提高storage节点的并发?我看storage磁盘的r/s才到8000不到。

<!-- gh-comment-id:3964273044 --> @lcxywfe commented on GitHub (Feb 26, 2026): > 能观察到 concurrent_user_calls 有很大的差异,其他结果应该也是并发不同导致的。我看你的代码每个 batch 里都会注册一遍文件,这样 batch 大小不同时注册的数量就不一样。你可以考虑一次性注册完,这样不对测试产生影响,然后给核心 IO 和其他操作加上 latency 记录。 @SF-Zhou 感谢回复! 确实排除register_fd之后,两者的速度差距缩小了,40GB/s和38GB/s,concurrent_user_calls 基本一致。 但我有的场景确实需要每次读不同的文件,有办法通过线程数量的提升来提高并发吗(除了`max_idle_threads`之外)。 此外如果block进一步降低,变成256KB x 64,concurrent_user_calls依然和之前一致,但速度会明显下降(23GB/s). 观察到bytes_per_user_call和bytes_per_user_request在block降低时同比例降低。 但是ops_per_user_call和ops_per_request在block从1MB降低到512KB时是同比例降低的,从512KB降低到256KB时几乎没有降低。 是否说明block的太小会导致访问太碎,ops上升,请问这个问题有办法解决吗?比如提高storage节点的并发?我看storage磁盘的r/s才到8000不到。
Author
Owner

@SF-Zhou commented on GitHub (Feb 26, 2026):

访问太多小的 block 确实会导致吞吐下降。主要是每个 op 都需要独立的查询路由、确定访问的节点、按节点聚合请求等,client 这里会有 overhead,增加 storage 应该没啥效果。目前没有特别好的办法。

<!-- gh-comment-id:3964338405 --> @SF-Zhou commented on GitHub (Feb 26, 2026): 访问太多小的 block 确实会导致吞吐下降。主要是每个 op 都需要独立的查询路由、确定访问的节点、按节点聚合请求等,client 这里会有 overhead,增加 storage 应该没啥效果。目前没有特别好的办法。
Author
Owner

@lcxywfe commented on GitHub (Feb 26, 2026):

我发现提高client的max_concuttent_requests和max_corrent_requests_per_server能加速,不知道这两个值给得太大会影响整个cluster的性能吗

<!-- gh-comment-id:3964341446 --> @lcxywfe commented on GitHub (Feb 26, 2026): 我发现提高client的max_concuttent_requests和max_corrent_requests_per_server能加速,不知道这两个值给得太大会影响整个cluster的性能吗
Author
Owner

@SF-Zhou commented on GitHub (Feb 26, 2026):

这两个参数是可以调整的。可以观察 inflight_requests metrics,如果压测时已经接近上限,那么调大这两个值有助于提高 inflight_requests,进而提高吞吐。还有 max_concurrent_transmission 可以同步调整。但这三个值如果过大,client 间的网络会更容易拥塞,所以按照你的集群规模和实际压测做调整。

<!-- gh-comment-id:3964379330 --> @SF-Zhou commented on GitHub (Feb 26, 2026): 这两个参数是可以调整的。可以观察 inflight_requests metrics,如果压测时已经接近上限,那么调大这两个值有助于提高 inflight_requests,进而提高吞吐。还有 max_concurrent_transmission 可以同步调整。但这三个值如果过大,client 间的网络会更容易拥塞,所以按照你的集群规模和实际压测做调整。
Author
Owner

@lcxywfe commented on GitHub (Feb 26, 2026):

好的,感谢感谢,那个register_fd慢的问题有办法吗,或者register_fd会占用很多内存之类的资源吗,假如我有10w个文件可考虑先全部register好再随机读吗

<!-- gh-comment-id:3964398740 --> @lcxywfe commented on GitHub (Feb 26, 2026): 好的,感谢感谢,那个register_fd慢的问题有办法吗,或者register_fd会占用很多内存之类的资源吗,假如我有10w个文件可考虑先全部register好再随机读吗
Author
Owner

@SF-Zhou commented on GitHub (Feb 26, 2026):

好的,感谢感谢,那个register_fd慢的问题有办法吗,或者register_fd会占用很多内存之类的资源吗,假如我有10w个文件可考虑先全部register好再随机读吗

推荐先全部注册好。

<!-- gh-comment-id:3964457702 --> @SF-Zhou commented on GitHub (Feb 26, 2026): > 好的,感谢感谢,那个register_fd慢的问题有办法吗,或者register_fd会占用很多内存之类的资源吗,假如我有10w个文件可考虑先全部register好再随机读吗 推荐先全部注册好。
Author
Owner

@lcxywfe commented on GitHub (Mar 18, 2026):

@SF-Zhou 您好,再请问一下,我单个client启动任务能打满client侧的ib,但是多个client启动之后,每个client的速度都打不满ib了,但是整体的吞吐远远没到storage节点的瓶颈,这个应该排查哪些指标?

<!-- gh-comment-id:4081147729 --> @lcxywfe commented on GitHub (Mar 18, 2026): @SF-Zhou 您好,再请问一下,我单个client启动任务能打满client侧的ib,但是多个client启动之后,每个client的速度都打不满ib了,但是整体的吞吐远远没到storage节点的瓶颈,这个应该排查哪些指标?
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
deepseek-ai/3FS#232
No description provided.