СоНоты

Еще более быстрая memcpy

Еще более быстрый вариант функции memcpy а-ля Duff’s device и используя конвеер (предыдущий вариант). Результаты сравнения времени выполнения теста, сравнивающего новый и старый варианты и стандартную библиотечную реализацию на невыровненных данных:

test0: FreeBSD memcpy in C 2.7686
test1: <new dps_memcpy>    0.43485
test2: <old dps_memcpy>    2.50218
test3: <standard memcpy>   0.456584
ratio(1/2): 0.17
ratio(1/0): 0.16
ratio(2/0): 0.90
ratio(1/3): 0.95


Результаты для выровненных данных:

test0: FreeBSD memcpy in C 0.0575361
test1: <new dps_memcpy>    0.022099
test2: <old dps_memcpy>    0.290257
test3: <standard memcpy>   0.00528717
ratio(1/2): 0.08
ratio(1/0): 0.38
ratio(2/0): 5.04
ratio(1/3): 4.18

Результаты приведены для FreeBSD 7.1 на процессоре Intel Duo E8400 3MHz. Для невыровненных данных, новый вариант примерно в 5 раз быстрее старого и чуть-чуть быстрее стандартной memcpy во FreeBSD (реализованной на ассемблере, но использующей выравнивание на границу целого). Для выровненных данных, новый вариант примерно в 10 раз быстрее старого, но примерно в 4 раза медленнее ассемблерной реализации функции во FreeBSD (но превосходит её сишный эквивалент в 2,5 раза).


typedef long long word;  // up to 32 bytes long
#define wsize sizeof(word)
#define wmask (wsize - 1)

void dps_minimove_forward(char *dst, const char *src, size_t t) {
	if (t) { dst[0] = src[0]; 
	if (t > 1) { dst[1] = src[1]; 
	if (t > 2) { dst[2] = src[2]; 
	if (t > 3) { dst[3] = src[3]; 
	if (t > 4) { dst[4] = src[4]; 
	if (t > 5) { dst[5] = src[5]; 
	if (t > 6) { dst[6] = src[6]; 
	if (t > 7) { dst[7] = src[7]; 
	if (t > 8 ) { dst[8] = src[8]; 
	if (t > 9) { dst[9] = src[9]; 
	if (t > 10) { dst[10] = src[10]; 
	if (t > 11) { dst[11] = src[11]; 
	if (t > 12) { dst[12] = src[12]; 
	if (t > 13) { dst[13] = src[13]; 
	if (t > 14) { dst[14] = src[14]; 
	if (t > 15) { dst[15] = src[15]; 
	if (t > 16) { dst[16] = src[16]; 
	if (t > 17) { dst[17] = src[17]; 
	if (t > 18) { dst[18] = src[18]; 
	if (t > 19) { dst[19] = src[19]; 
	if (t > 20) { dst[20] = src[20]; 
	if (t > 21) { dst[21] = src[21]; 
	if (t > 22) { dst[22] = src[22]; 
	if (t > 23) { dst[23] = src[23]; 
	if (t > 24) { dst[24] = src[24]; 
	if (t > 25) { dst[25] = src[25]; 
	if (t > 26) { dst[26] = src[26]; 
	if (t > 27) { dst[27] = src[27]; 
	if (t > 28) { dst[28] = src[28]; 
	if (t > 29) { dst[29] = src[29]; 
	if (t > 30) { dst[30] = src[30]; 
	}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}

void dps_minimove_backward(char *dst, char *src, size_t t) {
	switch(t) {
	case 31: dst[30] = src[30];
	case 30: dst[29] = src[29];
	case 29: dst[28] = src[28];
	case 28: dst[27] = src[27];
	case 27: dst[26] = src[26];
	case 26: dst[25] = src[25];
	case 25: dst[24] = src[24];
	case 24: dst[23] = src[23];
	case 23: dst[22] = src[22];
	case 22: dst[21] = src[21];
	case 21: dst[20] = src[20];
	case 20: dst[19] = src[19];
	case 19: dst[18] = src[18];
	case 18: dst[17] = src[17];
	case 17: dst[16] = src[16];
	case 16: dst[15] = src[15];
	case 15: dst[14] = src[14];
	case 14: dst[13] = src[13];
	case 13: dst[12] = src[12];
	case 12: dst[11] = src[11];
	case 11: dst[10] = src[10];
	case 10: dst[9] = src[9];
	case 9: dst[8] = src[8];
	case 8: dst[7] = src[7];
	case 7: dst[6] = src[6];
	case 6: dst[5] = src[5];
	case 5: dst[4] = src[4];
	case 4: dst[3] = src[3];
	case 3: dst[2] = src[2];
	case 2: dst[1] = src[1];
	case 1: dst[0] = src[0];
	}
}

void * dps_memcpy_new(char *dst0, char *src0, size_t length) {
	size_t t;

  if (length == 0 || dst0 == src0)		/* nothing to do */
    return dst0;
  if ((unsigned long long)dst0 < (unsigned long long)src0) { /* copy forward */
    register char *dst = dst0, *src = src0;
    t = (unsigned int)src & wmask;
    if (t) {
    	if (length < wsize) {
		t = length;
    	} else {
		t = wsize - t;
    	}
	dps_minimove_forward(dst, src, t);
	length -= t;
	src += t; dst += t;
     }
     t = length / wsize;
     if (t) {
	register size_t n = (t + 7) / 8;
    	register size_t r = (t % 8);
	register word *wdst = (word*)dst, *wsrc = (word*)src;
    	if (r == 0) r = 8;
    	wdst[0] = wsrc[0];
    	if (r > 1) { wdst[1] = wsrc[1];
    	if (r > 2) { wdst[2] = wsrc[2];
    	if (r > 3) { wdst[3] = wsrc[3];
    	if (r > 4) { wdst[4] = wsrc[4];
    	if (r > 5) { wdst[5] = wsrc[5];
    	if (r > 6) { wdst[6] = wsrc[6];
    	if (r > 7) { wdst[7] = wsrc[7];
	}}}}}}}
    	wsrc += r; wdst += r;
    	while (--n > 0) {
    		wdst[0] = wsrc[0];
    		wdst[1] = wsrc[1];
    		wdst[2] = wsrc[2];
    		wdst[3] = wsrc[3];
    		wdst[4] = wsrc[4];
    		wdst[5] = wsrc[5];
    		wdst[6] = wsrc[6];
    		wdst[7] = wsrc[7];
    		wsrc += 8; wdst += 8;
    	}
 	dst = (char*)wdst; src = (char *)wsrc;
    }
    if ( (t = (length & wmask)) ) dps_minimove_forward(dst, src, t);

  } else { /* copy backward */
    register char *dst = dst0 + length, *src = src0 + length;
    t = (unsigned int)src & wmask;
    if (t) {
	if (length < wsize) {
		t = length;
	}
	dst -= t; src -= t;
	length -= t;
	dps_minimove_backward(dst, src, t);
    }
    t = length / wsize;
    if (t) {
    	register size_t n = (t + 7) / 8;
    	register size_t r = (t % 8);
	register word *wdst = (word*)dst, *wsrc = (word*)src;
    	if (r == 0) r = 8;
	wsrc -= r; wdst -= r;
	switch(r) {
	case 8:wdst[7] = wsrc[7];
	case 7:wdst[6] = wsrc[6];
	case 6:wdst[5] = wsrc[5];
	case 5:wdst[4] = wsrc[4];
	case 4:wdst[3] = wsrc[3];
	case 3:wdst[2] = wsrc[2];
	case 2:wdst[1] = wsrc[1];
	case 1:wdst[0] = wsrc[0];
	}
     	while (--n > 0) {
		wsrc -= 8; wdst -= 8;
		wdst[7] = wsrc[7];
		wdst[6] = wsrc[6];
		wdst[5] = wsrc[5];
		wdst[4] = wsrc[4];
		wdst[3] = wsrc[3];
		wdst[2] = wsrc[2];
		wdst[1] = wsrc[1];
		wdst[0] = wsrc[0];
	}
	dst = (char*)wdst; src = (char*)wsrc;
    }
    t = length & wmask;
    dps_minimove_backward(dst - t, src - t, t);
 }
  return dst0;
}