如何在 Linux 下用 C/C++ 达到 10GB/s 写入 NVMe 磁盘

问题描述 投票:0回答:1

我正在考虑设计一个在 Linux(当前是 Ubuntu 20.04)下运行的 C/C++ 应用程序。应用程序应该能够以 1-10GB/s 左右的速率连续存储数据,最好是在文件系统上,但这不是强制性的。为此,我在系统中有四个 NVMe 磁盘组织为软件 RAID0,理论最大传输速率应约为 12GB/s。

首先,我尝试在其他 NVMe 和 RAID 之间复制文件,速率约为 1-2GB/s。然后我编写了一个简单的 c/c++ 程序,使用标准的“write”命令,但没有任何改进。

然后我意识到Ubuntu中有一个工具,gnome-disks。当我进行读写基准测试时,我达到了大约 10-11GB/s。我意识到该实用程序不使用文件系统,而是自行写入磁盘和从磁盘写入数据,并利用 NVMe 磁盘可以并行执行操作的事实。

查看 gnome-disks 的源代码,我发现该实用程序也使用“write”来写入磁盘。通过查看 gnome-disks 中的 strace,我猜想 write 被 poll、writev 和可能的 recvmsg 取代。

NVMe磁盘应该如何在c/c++中使用才能获得良好的性能(gnome-disks是如何做到的?)?像 libnvme、SPDK、aio、udisks2 这样的库?


我刚刚制作了一个简短的程序,使用 pwritev2 而不是 write。这带来了巨大的改进,我现在达到了 12GB/s 左右的写入速度。代码如下。通过提供要测试的设备的路径来编译并运行它,例如速度/dev/md/raid0

该程序会破坏设备上的数据,所以要小心。

#include <sys/uio.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>

#include <math.h>

#include <time.h>
#include <errno.h>

#define LOOPS (20)
#define BUFFERS (8)
#define BUFFER_SIZE (250*1024*1024)

#error Do not run this program on a device where you have data you want to keep. This program will destroy the data!
int main(int argc, char *argv[])
{
    if( argc == 2 )
    {
        int fd = open(argv[1], O_RDWR | O_DIRECT);

        if( fd > -1 )
        {
            ssize_t page_size;
            char *buffers_unaligned[BUFFERS];
            iovec iov[BUFFERS];
        
            page_size = sysconf(_SC_PAGESIZE);

            for( int i=0; i<BUFFERS; i++)
            {
                buffers_unaligned[i] = (char*)malloc( BUFFER_SIZE + page_size );
                
                if( buffers_unaligned[i] == NULL )
                {
                    perror("Unable to allocate memmory");
                    exit(EXIT_FAILURE);
                }
                else
                {
                    iov[i].iov_base = (char*)(((long)buffers_unaligned[i] + page_size) & (~(page_size-1)));
                    memset( iov[i].iov_base, i, BUFFER_SIZE );
                    iov[i].iov_len = BUFFER_SIZE;
                }
            }

            struct timespec start, stop, elapsed;
            ssize_t nwritten = 0;
       
            clock_gettime(CLOCK_MONOTONIC, &start);

            for( int loop=0; loop<LOOPS; loop++ )
                nwritten += pwritev2(fd, iov, BUFFERS, nwritten, RWF_HIPRI);

            fsync(fd);

            clock_gettime(CLOCK_MONOTONIC, &stop);

            printf("Written bytes: %zu\n", nwritten);
        
            if((stop.tv_sec - start.tv_sec) < 0)
            {
                elapsed.tv_sec = stop.tv_sec - start.tv_sec -1;
                elapsed.tv_nsec = stop.tv_nsec - start.tv_nsec + pow(10,9);
            }
            else
            {
                elapsed.tv_sec = stop.tv_sec - start.tv_sec;
                elapsed.tv_nsec = stop.tv_nsec - start.tv_nsec;
            }
        
            unsigned long nsec = elapsed.tv_sec*pow(10,9) + elapsed.tv_nsec;
        
            printf("Time: %luns\n", nsec);
        
            double speed = (double)nwritten/nsec;
        
            printf("Speed: %f GB/s\n", speed);
            close(fd);

            for( int i=0; i<BUFFERS; i++)
            {
                free(buffers_unaligned[i]);
                buffers_unaligned[i] = NULL;
            }
        }
    }
    else
    {
        perror("Error with open: ");
    }
        
    return 0;
}
c++ linux file-io gnome nvme
1个回答
0
投票

猜测您正在寻找一种方法来使您的系统忙于访问您的存储设备。 尝试使用 NVMe 测试工具 Penetrate (https://www.cellcomputers.com.tw/penetrate.html)。它可以轻松地在 Gen4 x4 nvme SSD 上实现 7 GB/s 的吞吐量。它也可以在多个设备上运行。该工具运行在Ubuntu 20.04上。您可以从网站下载它,并且有用于您描述的测试的现成脚本。

© www.soinside.com 2019 - 2024. All rights reserved.