Флейм
GameDev.ru / Флейм / Форум / Contra Force Advance (11 стр)

Contra Force Advance (11 стр)

Страницы: 110 11 12 13 14 Следующая »
=A=L=X=Постоялецwww7 июля 20189:42#150
А ну да, SSE даёт еще прирост в 2.8 раза. :) То есть по сравнению с CMOV. То есть по сравнению с таблицей векторизированный в SSE код быстрее в 5,7 раза.
Нужно использовать код который я выше демонстрировал как монструозную оптимизацию от GCC, при этом нужны опции -march=core2 и -O3.
Получается у него как то так (инлайнит зная размер массива заранее):
  xor  edx, edx
  xor  eax, eax
L85:
  pxor  xmm0, xmm0
  add  edx, 1
  mov  esi, 524288
  add  eax, 16
  pcmpeqb  xmm0, XMMWORD PTR _A[eax-16]
  movdqa  xmm1, XMMWORD PTR _B[eax-16]
  pand  xmm1, xmm0
  pandn  xmm0, XMMWORD PTR _A[eax-16]
  por  xmm0, xmm1
  movaps  XMMWORD PTR _B[eax-16], xmm0
  cmp  esi, edx
  ja  L85
Ghost2Постоялецwww7 июля 201810:26#151
=A=L=X=

1. pandn тут не нужен. Правка в #148 как раз посвящена этому ;)
2. Ты на интринсиках пишешь?

=A=L=X=Постоялецwww7 июля 201812:35#152
Ghost2
Это компилятор нагенерил из кода на прошлой странице где я ржал как он развернул циклы. Когда он знает cnt, то генерирует намного меньше кода в инлайне.
Ghost2Постоялецwww7 июля 201813:12#153
=A=L=X=

Странно тогда, что он andnot оставил, оптимизаторы пока не идеальны. Ещё раз убеждаюсь, что такие вещи лучше писать на интринсиках.

> Когда он знает cnt
В некоторых компиляторах есть прагмы, задающие loop count или min/max/module для циклов.

Правка: 7 июля 2018 13:13

=A=L=X=Постоялецwww7 июля 201813:30#154
Ghost2
> Ещё раз убеждаюсь, что такие вещи лучше писать на интринсиках.
Угу.
Сделал такой код, чтобы было чище и прозрачнее:
+ Показать

С опциями -march=core2 -ffast-math -O3 -masm=intel -std=gnu++14 вывод у меня на Phenom II x4 3.5GHz получается следующей:
%probab.;blt_if  ;blt_tern;blt_tbl ;blt_cmov
0.000000;0.102000;0.041000;0.222000;0.111000
0.050000;0.130000;0.040000;0.224000;0.116000
0.100000;0.153000;0.041000;0.223000;0.111000
0.150000;0.180000;0.041000;0.224000;0.116000
0.200000;0.204000;0.040000;0.222000;0.116000
0.250000;0.228000;0.041000;0.223000;0.116000
0.300000;0.253000;0.041000;0.223000;0.108000
0.350000;0.271000;0.040000;0.222000;0.110000
0.400000;0.286000;0.041000;0.221000;0.111000
0.450000;0.297000;0.041000;0.223000;0.111000
0.500000;0.299000;0.041000;0.220000;0.115000
0.550000;0.295000;0.041000;0.220000;0.124000
0.600000;0.279000;0.042000;0.224000;0.109000
0.650000;0.261000;0.043000;0.219000;0.109000
0.700000;0.240000;0.050000;0.223000;0.108000
0.750000;0.216000;0.043000;0.221000;0.112000
0.800000;0.188000;0.042000;0.221000;0.112000
0.850000;0.160000;0.041000;0.222000;0.114000
0.900000;0.133000;0.041000;0.220000;0.114000
0.950000;0.107000;0.041000;0.220000;0.113000
1.000000;0.083000;0.041000;0.221000;0.110000
То есть if/бранч самый в среднем тормоз и единственный у кого картина быстродействия зависит от вероятности успешного предсказания.
Табличный метод не зависит от данных/предсказаний, но в среднем всё равно тормоз и нередко уступает бранчу. В лучшем случае он быстрее бранча в 1,36 раз.
Вручную запрограммированый cmov тоже ни от чего не зависит и держится огурцом - быстрее бранча почти всегда, а в лучшем случае обгоняет бранч в 2,6 раза.
Но лидером является вариант с тернарным условным оператором - он резко вырывается вперед, не зависит от данных и быстрее бранча в 7,3 раза.
То есть между этим:
    if ( *src )
      *dst = *src;
и этим:
    *dst = (*src ? *src : *dst);
разница на порядок!
Вскрытие показывает, что тернарный оператор в отличие от if-а в данном исполнении кода компилится в вышеприведенные функции SSE.
Окай. Чуток снижаем оптимизацию в связи с её агрессивностью - код пухнет как на дрожжах, видно что он, например разворачивает циклы по ITER, что не имеет смысла и увеличивает код. Так что меняем -O3 на -O2 и... blt_tern превращается в тыкву - начинает работать в точности так же медленно и зависимо от данных как и if, потому что на уровне асма в него и превращается.
Вот так вот.
Мораль: нужно делать на SSE и либо на интрсинках либо на инлайн-асме, если не хочется получать просадок в десяток раз из-за прихотей компилятора.

Правка: 7 июля 2018 14:52

FordPerfectПостоялецwww7 июля 201814:48#155
=A=L=X=
> -masm=intel
Басурман.

Размер слишком большой - тебе эффекты кеша не слишком интересно мерить, они картину замазывают.

Ну и код можно было бы на rextester выложить.
http://rextester.com/IHNQ89244
https://godbolt.org/g/n2vA96

=A=L=X=Постоялецwww7 июля 201814:55#156
FordPerfect
> Размер слишком большой - тебе эффекты кеша не слишком интересно мерить

Я снижал размер до 1Мб - вполовину меньше, чем FullHD 8-битный, картина слабо меняется. Как раз не хотелось мерять кеш.
Код убрал под спойлер.

FordPerfectПостоялецwww7 июля 201815:26#157
> *dst = (*src ? *src : *dst);
А тю, я фигово прочитал. Прозрачность в src.

Тогда:
http://rextester.com/SEYF89725

%probab. | blt_if   | blt_tern | blt_tbl  | blt_cmov | blt_bitw | blt_sse2
0.000000 |     0.75 |     0.66 |     0.92 |     0.85 |     0.42 |     0.05
0.050000 |     1.33 |     1.14 |     0.88 |     0.87 |     0.41 |     0.05
0.100000 |     1.74 |     1.43 |     0.87 |     0.96 |     0.41 |     0.05
0.150000 |     2.22 |     1.82 |     0.87 |     0.84 |     0.43 |     0.05
0.200000 |     2.48 |     2.14 |     0.87 |     0.85 |     0.41 |     0.05
0.250000 |     2.88 |     2.61 |     0.89 |     0.85 |     0.42 |     0.05
0.300000 |     3.25 |     2.98 |     0.86 |     0.85 |     0.41 |     0.05
0.350000 |     3.73 |     3.75 |     0.87 |     0.85 |     0.41 |     0.05
0.400000 |     4.06 |     4.04 |     0.91 |     0.88 |     0.41 |     0.05
0.450000 |     4.26 |     4.37 |     0.86 |     0.84 |     0.41 |     0.05
0.500000 |     4.22 |     4.44 |     0.88 |     0.87 |     0.41 |     0.05
0.550000 |     4.25 |     4.19 |     0.87 |     0.85 |     0.41 |     0.05
0.600000 |     4.10 |     4.02 |     0.86 |     0.84 |     0.41 |     0.05
0.650000 |     3.69 |     3.51 |     0.86 |     1.21 |     0.71 |     0.09
0.700000 |     3.45 |     3.17 |     1.28 |     1.06 |     0.41 |     0.05
0.750000 |     2.71 |     2.97 |     0.86 |     0.84 |     0.41 |     0.05
0.800000 |     2.27 |     2.44 |     0.86 |     0.84 |     0.41 |     0.05
0.850000 |     1.78 |     2.02 |     0.86 |     0.85 |     0.41 |     0.05
0.900000 |     1.40 |     1.77 |     0.86 |     0.84 |     0.41 |     0.05
0.950000 |     1.05 |     1.26 |     0.86 |     0.84 |     0.41 |     0.05
1.000000 |     0.60 |     0.78 |     0.97 |     0.84 |     0.41 |     0.05

-O2 vs -O3 особо не влияет.

Можно наверно лучше.

Мегабайт - это тоже больше L1, но вроде и в самом деле разницы мало.

=A=L=X=Постоялецwww7 июля 201815:31#158
P.S.
Ну да, когда всё влазит в кеш - SSE вообще рвёт и мечет за счёт того, что сразу перегоняет по 16 байт за присест и никого не ждёт.
Худшие случи у бранчей обгоняются в грёбанные 54 раза у меня. xD Ндаа... Но это конечно кеш, не факт что на реальных данных оно будет близко к такому вырыванию очка.
Ghost2Постоялецwww7 июля 201819:27#159
=A=L=X=

> оно будет близко к такому вырыванию очка
Близко. И очень близко, если данные разложены в памяти удобно для проца.

FordPerfectПостоялецwww7 июля 201820:06#160
Не, кеш таки влияет, даже если подряд обрабатывать.
http://rextester.com/RQK63343
+ 16 KB
+ 2 MB
Ghost2Постоялецwww8 июля 20180:33#161
FordPerfect

Влияет конечно. Иначе он не нужен был бы ) Все равно вектор на уровне байт/слов обычно в десятки раз быстрее влобных подходов, если алгоритм удачно на него ложится.

=A=L=X=Постоялецwww8 июля 20185:44#162
Функцию test следует переделать следующим образом чтобы она тестировала результаты с эталоном:
void test( void (*f)( const unsigned char * __restrict, unsigned char * __restrict, size_t ) )
{
        clock_t before, after;

        for ( int i = 0; i < A_SIZE; i++ )
        {
          unsigned char t = rand();
          B[ i ] = t;
          C[ i ] = t;
        };
        blt_if( A, B, A_SIZE );
        f( A, C, A_SIZE ); // precache

        before = clock();
        for ( int i = 0; i < ITERS; i++ )
          f( A, C, A_SIZE );
        after = clock();
        // In ns/pixel.
        printf(" | %8.2f", 1.0e+9*(double)(after - before) / (double)CLOCKS_PER_SEC / (double)A_SIZE / (double) ITERS);

  if ( memcmp( B, C, A_SIZE ) != 0 )
  {
    printf( "\nfailed!\n" );
          fflush(stdout);
          exit( 0 );
  };

        fflush(stdout);
};
Даже последний вариант SSE сейчас показывает "failed!", поэтому нельзя было верить показаниям, может там даже не весь буфер обрабатывался. Я не настолько разбираюсь чтобы исправлять SSE.

Правка: 8 июля 2018 5:48

FordPerfectПостоялецwww8 июля 201811:14#163
=A=L=X=
О, спасибо что заметил.
Я всё-ещё dst с ключом сравнивал, сорри.
http://rextester.com/OMVZX95227

Конкретно в SSE вроде и разбираться особо не требуется.

*Lain*Постоялецwww8 июля 201811:38#164
FordPerfect
а как ты ссе авх делаешь? просто memcpy в ссе\авх тип данных, а дальше над ним проводишь обычные или интерсик операции и компилятор понимает, что ему нужно векторные операции юзать?
А в мсвц также?
А мсвц поймет мемсру также красиво, как подсказку компилятору?
Страницы: 110 11 12 13 14 Следующая »

/ Форум / Флейм / ПроЭкты

2001—2018 © GameDev.ru — Разработка игр