读、写、擦除是SSD对NAND的三大基本操作,但是针对NAND自身的特性和多样化的I/O模型,SSD怎么读、写、擦除是门高级艺术,也由此衍生了很多技术。

比如垃圾回收(GC),一个优质的GC明白在什么时候,挑选哪些Block,将上面的数据搬到哪去。

trim_trim函数怎么用_phptrim

GC的基本原理

(如上图)左侧两个Block中的有效数据被搬移/整合到一个新的Block,然后这两个Block将被擦除,形成两个可以写入数据的新Block。

关于GC的研究,可以看金一同学的这个文章。

《》

有个问题,GC怎么才能知道垃圾是垃圾呢?讨论这个问题需要先回到数据存储的过程,开始是这样的。

当有数据删除之后,SSD并不能及时的知道谁是脏数据。形成了这样的局面

trim函数怎么用_phptrim_trim

只有操作系统往标注DEL的位置上写数据时,盘才知道这是垃圾。

不然这些数据将一直被GC认为是有效数据搬移。为了更高效的执行GC,让操作系统和文件系统高效的和SSD主控交流删除文件的信息。

就出现了TRIM。

trim函数怎么用_phptrim_trim

A trim (known as TRIM in the ATA set, and UNMAP in the SCSI set) an to a solid-state drive (SSD) which of data are no in use and can be wiped .

——()

维基百科给的这段介绍,在NVMe SSD上解释就是,TRIM让操作系统通过Trim命令(NVMe协议中有定义)告诉SSD哪些地址上的数据可以擦除,从而提升垃圾回收的效率。

就是这么干的,

我们总结了TRIM的三大价值:

降低写放大

提升写性能

提高设备寿命

phptrim_trim函数怎么用_trim

对于NVMe来说,Trim是让GC长了一对翅膀。说着简单trim,但是要通过Trim达到降低写放大的目标而不影响设备性能,其中NAND无效块的擦写时机、Tirm和其他一系列SSD核心算法的配合非常有讲究。

phptrim_trim函数怎么用_trim

TRIM实现难在哪这里就不详细讨论了,有兴趣的可以看Ron写的文章《SSD Trim 详解》(识别下面二维码获取):

NVMe Spec对TRIM命令有详细的规定,所以使NVMe Cli就可以对TRIM功能进行验证

trim_trim函数怎么用_phptrim

接下来就结合NVMe Spec的规定trim,发出我们的一条TRIM命令,然后做个验证。

准备设备和环境

使用官网 910/916系列产品中的4T U.2NVMe进行验证测试。

nvme list的信息如下 :

写入数据:从NVMe 10GiB的位置往后写10G的数据,数据是

[root@localhost ~]# fio --thread --direct=1 --allow_file_creat=0 --ioengine=libaio --rw=write --bs=128k --iodepth=128 --numjobs=1 --name=nvme0n1 --filename=/dev/nvme0n1 --offset=10g --size=10g --verify=pattern --do_verify=0 --verify_pattern=0x12345678 
nvme0n1: (g=0): rw=write, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=libaio, iodepth=128
fio-3.12
Starting 1 thread
Jobs: 1 (f=1): [W(1)][-.-%][w=3165MiB/s][w=25.3k IOPS][eta 00m:00s]
nvme0n1: (groupid=0, jobs=1): err= 0: pid=4787: Mon Mar  4 15:59:35 2019
  write: IOPS=25.2k, BW=3156MiB/s (3309MB/s)(10.0GiB/3245msec)
    slat (nsec): min=3204, max=81924, avg=9364.20, stdev=2437.54
    clat (usec): min=1177, max=17333, avg=5058.82, stdev=387.21
     lat (usec): min=1187, max=17347, avg=5068.25, stdev=387.16
    clat percentiles (usec):
     |  1.00th=[ 4555],  5.00th=[ 5014], 10.00th=[ 5014], 20.00th=[ 5014],
     | 30.00th=[ 5014], 40.00th=[ 5014], 50.00th=[ 5080], 60.00th=[ 5080],
     | 70.00th=[ 5080], 80.00th=[ 5080], 90.00th=[ 5080], 95.00th=[ 5080],
     | 99.00th=[ 5145], 99.50th=[ 6063], 99.90th=[11207], 99.95th=[13042],
     | 99.99th=[16909]
   bw (  MiB/s): min= 3145, max= 3166, per=100.00%, avg=3157.17, stdev= 7.59, samples=6
   iops        : min=25160, max=25334, avg=25257.33, stdev=60.68, samples=6
  lat (msec)   : 2=0.11%, 4=0.48%, 10=99.30%, 20=0.11%
  cpu          : usr=3.73%, sys=25.09%, ctx=74895, majf=0, minf=13
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=99.9%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.1%
     issued rwts: total=0,81920,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=128

Run status group 0 (all jobs):
  WRITE: bw=3156MiB/s (3309MB/s), 3156MiB/s-3156MiB/s (3309MB/s-3309MB/s), io=10.0GiB (10.7GB), run=3245-3245msec

Disk stats (read/write):
  nvme0n1: ios=62/79409, merge=0/0, ticks=1/401029, in_queue=401403, util=96.86%
[root@localhost ~]#

依据NVMe Spec准备测试文件和命令

使用将这10G的数据trim掉,利用强大的命令跟踪一下的耗时,粗略的计算下Trim的速度。

首先需要按照NVMe Spec协议中的 设置(如下图),创建一个的二进制文件。一个range最多(* 大约)的LBA。

in 和 LBA的计算

10G数据按照的格式得出是:

10*1024*1024*1024/512=个(LBA)

Fio中=10g,因此start LBA也是 :

10*1024*1024*1024/512=

利用创建二进制文件代码如下(简单的写个)

import array

buf = array.array("B",[0x00] * 4096)

def setValue(buf, offset, num, value):
#Common function to set unsigned integer value within the given range
#offset & num are in bytes for this function series.
    if num == 1:
        buf[offset] = value
    else:
        for i in xrange(num):
            buf[offset+i] = (value>>(8*i)) & 0xff
    return buf

def writeBinaryFile(buf, filepath):
    with open(filepath, "wb"as f:
        buf.tofile(f)

#Length in logical blocks :offset=4 num=4 value=20971520
buf = setValue(buf,4,420971520)

# Starting LBA  offset=8 num=8 value=20971520
buf = setValue(buf,8,820971520)

writeBinaryFile(buf, “/root/trim.bin”)

下面是NVMe Spec里TRIM命令的规范,使用 发送NVMe 需要照着填写

:二进制:0b 0100=0x04

准备工作做足了,接下来就是发!

发TRIM时候,在命令前加上 –ttt,可以粗略的计算TRIM的速度。

[root@localhost ~]# strace –ttt nvme io-passthru /dev/nvme0 --opcode=0x09 --namespace-id=1 --cdw10=0x00 --cdw11=0x04 --input-file=trim.bin --data-len=4096 –write

我们经过计算得出TRIM耗时0.,所以Trim的速度大约是:10 /0.00124= 8064./s 大约8TB/s

TRIM后立即数据验证

[root@localhost ~]# fio --thread --direct=1 --allow_file_creat=0 --ioengine=libaio --rw=read --bs=128k --iodepth=128 --numjobs=1 --name=nvme0n1 --filename=/dev/nvme0n1 --offset=10g --size=10g --verify=pattern --do_verify=1 --verify_pattern=0x00
nvme0n1: (g=0): rw=read, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=libaio, iodepth=128
fio-3.12
Starting 1 thread
Jobs: 1 (f=1): [V(1)][-.-%][r=3176MiB/s][r=25.4k IOPS][eta 00m:00s]
nvme0n1: (groupid=0, jobs=1): err= 0: pid=9241: Mon Mar  4 21:37:35 2019
  read: IOPS=25.4k, BW=3171MiB/s (3325MB/s)(10.0GiB/3229msec)
    slat (usec): min=9, max=225, avg=11.21, stdev= 3.14
    clat (usec): min=1938, max=11585, avg=5010.57, stdev=1313.65
     lat (usec): min=1948, max=11595, avg=5021.85, stdev=1313.57
    clat percentiles (usec):
     |  1.00th=[ 2147],  5.00th=[ 2868], 10.00th=[ 3294], 20.00th=[ 3884],
     | 30.00th=[ 4293], 40.00th=[ 4621], 50.00th=[ 4948], 60.00th=[ 5342],
     | 70.00th=[ 5735], 80.00th=[ 6194], 90.00th=[ 6783], 95.00th=[ 7242],
     | 99.00th=[ 8029], 99.50th=[ 8225], 99.90th=[ 8717], 99.95th=[ 9110],
     | 99.99th=[ 9765]
   bw (  MiB/s): min= 3168, max= 3176, per=100.00%, avg=3172.88, stdev= 3.26, samples=6
   iops        : min=25348, max=25410, avg=25383.00, stdev=26.07, samples=6
  lat (msec)   : 2=0.47%, 4=22.76%, 10=76.75%, 20=0.01%
  cpu          : usr=45.94%, sys=29.43%, ctx=22554, majf=0, minf=25
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=99.9%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.1%
     issued rwts: total=81920,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=128

Run status group 0 (all jobs):
   READ: bw=3171MiB/s (3325MB/s), 3171MiB/s-3171MiB/s (3325MB/s-3325MB/s), io=10.0GiB (10.7GB), run=3229-3229msec

Disk stats (read/write):
  nvme0n1: ios=79930/0, merge=0/0, ticks=394735/0, in_queue=395214, util=96.95%
[root@localhost ~]#

可以看到加上--=0x00之后,err= 0,证明TRIM后数据都变成0了,至于更细节的说明,在此不再赘述。

phptrim_trim函数怎么用_trim

通过一系列的介绍和实验验证,我们看到了TRIM的价值和实现原理。在TRIM的帮助下,NVMe SSD的GC等操作效率更高,进而达到降低写放大,提高产品性能和寿命的效果。


限时特惠:
本站持续每日更新海量各大内部创业课程,一年会员仅需要98元,全站资源免费下载
点击查看详情

站长微信:Jiucxh

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注