Re: [PATCH] Performance Improvement in CRC16 Calculations.

From: Christophe LEROY
Date: Thu Aug 16 2018 - 11:41:24 EST


Please include your new patch as plain text inside the mail, not as a MIME attachment. Otherwise it is not downloadable from


Le 16/08/2018 Ã 16:22, Douglas Gilbert a ÃcritÂ:
Rather than present this formerly as an alternate patch, attached is a
clean-up of my patch which uses the variable size table proposed by
Joe Perches <joe@xxxxxxxxxxx> and is based on the original patch that
started this thread.

Doug Gilbert

On 2018-08-16 10:02 AM, Jeffrey Lien wrote:
We did not test the slice by 4 or 8 tables. I'm not sure of the value of doing that since the slice by 16 will provide the best performance gain. If I'm missing anything here, please let me know.

I'm working on a new version of the patch based on the feedback from others and will also change the pointer variables to start with p and fix the indenting you mentioned below in the new version of the patch.


Jeff Lien

-----Original Message-----
From: Eric Biggers [mailto:ebiggers@xxxxxxxxxx]
Sent: Friday, August 10, 2018 3:16 PM
To: Jeffrey Lien <Jeff.Lien@xxxxxxx>
Cc: linux-kernel@xxxxxxxxxxxxxxx; linux-crypto@xxxxxxxxxxxxxxx; linux-block@xxxxxxxxxxxxxxx; linux-scsi@xxxxxxxxxxxxxxx; herbert@xxxxxxxxxxxxxxxxxxx; tim.c.chen@xxxxxxxxxxxxxxx; martin.petersen@xxxxxxxxxx; David Darrington <david.darrington@xxxxxxx>; Jeff Furlong <jeff.furlong@xxxxxxx>
Subject: Re: [PATCH] Performance Improvement in CRC16 Calculations.

On Fri, Aug 10, 2018 at 02:12:11PM -0500, Jeff Lien wrote:
This patch provides a performance improvement for the CRC16
calculations done in read/write workloads using the T10 Type 1/2/3
guard field. For example, today with sequential write workloads (one
thread/CPU of IO) we consume 100% of the CPU because of the CRC16
computation bottleneck. Today's block devices are considerably
faster, but the CRC16 calculation prevents folks from utilizing the
throughput of such devices. To speed up this calculation and expose
the block device throughput, we slice the old single byte for loop into a 16 byte for loop, with a larger CRC table to match. The result has shown 5x performance improvements on various big endian and little endian systems running the 4.18.0 kernel version.

FIO Sequential Write, 64K Block Size, Queue Depth 64
BE Base Kernel:ÂÂÂÂÂÂÂ bw=201.5 MiB/s
BE Modified CRC Calc:Â bw=968.1 MiB/s
4.80x performance improvement

LE Base Kernel:ÂÂÂÂÂÂÂ bw=357 MiB/s
LE Modified CRC Calc:Â bw=1964 MiB/s
5.51x performance improvement

FIO Sequential Read, 64K Block Size, Queue Depth 64
BE Base Kernel:ÂÂÂÂÂÂÂ bw=611.2 MiB/s
BE Modified CRC calc:Â bw=684.9 MiB/s
1.12x performance improvement

LE Base Kernel:ÂÂÂÂÂÂÂ bw=797 MiB/s
LE Modified CRC Calc:Â bw=2730 MiB/s
3.42x performance improvement

Did you also test the slice-by-4 (requires 2048-byte table) and slice-by-8 (requires 4096-byte table) methods? Your proposal is slice-by-16 (requires 8192-byte table); the original was slice-by-1 (requires 512-byte table).

 __u16 crc_t10dif_generic(__u16 crc, const unsigned char *buffer,
size_t len)Â {
-ÂÂÂ unsigned int i;
+ÂÂÂ const __u8 *i = (const __u8 *)buffer;
+ÂÂÂ const __u8 *i_end = i + len;
+ÂÂÂ const __u8 *i_last16 = i + (len / 16 * 16);

'i' is normally a loop counter, not a pointer.
Use 'p', 'p_end', and 'p_last16'.

-ÂÂÂ for (i = 0 ; i < len ; i++)
-ÂÂÂÂÂÂÂ crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff];
+ÂÂÂ for (; i < i_last16; i += 16) {
+ÂÂÂÂÂÂÂ crc = t10_dif_crc_table[15][i[0] ^ (__u8)(crc >>Â 8)] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[14][i[1] ^ (__u8)(crc >>Â 0)] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[13][i[2]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[12][i[3]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[11][i[4]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[10][i[5]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[9][i[6]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[8][i[7]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[7][i[8]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[6][i[9]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[5][i[10]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[4][i[11]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[3][i[12]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[2][i[13]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[1][i[14]] ^
+ÂÂÂÂÂÂÂ t10_dif_crc_table[0][i[15]];
+ÂÂÂ }

Please indent this properly.

ÂÂÂÂÂÂÂ crc = t10_dif_crc_table[15][i[0] ^ (__u8)(crc >>Â 8)] ^
ÂÂÂÂÂÂÂÂÂÂÂÂÂ t10_dif_crc_table[14][i[1] ^ (__u8)(crc >>Â 0)] ^
ÂÂÂÂÂÂÂÂÂÂÂÂÂ t10_dif_crc_table[13][i[2]] ^
ÂÂÂÂÂÂÂÂÂÂÂÂÂ t10_dif_crc_table[12][i[3]] ^
ÂÂÂÂÂÂÂÂÂÂÂÂÂ t10_dif_crc_table[11][i[4]] ^

- Eric