2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2
 
 Re: Помогите ускорить функцию на С
Сообщение25.09.2018, 15:49 


31/01/12
97
Кто-нибудь знает, можно ли распараллелить два "if" с помощью OpenMP, и даст ли это что-то?

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение25.09.2018, 15:52 
Заслуженный участник


09/05/12
25179
fan_of_algoritms в сообщении #1341369 писал(а):
Кто-нибудь знает, можно ли распараллелить два "if" с помощью OpenMP, и даст ли это что-то?
Распараллелить (формально) можно, и это даже даст - падение производительности. :-)

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 13:29 
Заслуженный участник


12/07/07
4522
В продолжение моего предыдущего сообщения. Под Win64 обычно SSE/AVX-ориентированные вычисления с плавающей точкой. Я провел простенькое тестирование трех вариантов под 64b Win 7. В случае доступного мне компилятора (Embarcadero RAD Studio XE7) стандартный логический тип имеет длину 8 бит; значение True кодируется одной 1, а False – нулями.
Для сравнения написано три крошечные функции. Функции имеют один аргумент типа числа двойной точности с плавающей запятой, который при вызове заносится в xmm0; не вызывают другие функции (листовые), результат логического типа возвращают в al. Функции Lt1 и Lt2 выполняют сравнение аргумента "на меньше" числа в переменной c, а функция Eq проверяет на равенство аргумента со значением в c.
Тело Lt1:
Код:
  comisd   xmm0, [c]
  setb     al

Тело Lt2:
Код:
  movsd    xmm1, [c]
  cmpsd    xmm0, xmm1, 001b
  movmskpd eax,  xmm0
До X7 в компиляторе был «баг movq»: например, по инструкции movq rax, xmm2 компилятор генерировал код, приводящий к перемещению данных из xmm2 в xmm0. В X7 этот баг вроде бы починили, но я столкнулся с тем что cmpsd xmm0, mem128, 001b в доступной мне версии компилятора не работает. Поэтому пришлось загружать значение из переменной c в xmm1.

Тело Eq:
Код:
  movq     rax,  xmm0
  xor      rax,  [c]
  setz     al

Для тестирования скоростей выполнения были созданы три процедуры: Lt1_tst, Lt2_tst, Eq_tst.
В теле этих процедур в цикле выполнялись соответствующие тела функций. Для имитации передачи параметра выполнялось копирование аргумента из переменной d в xmm0. Число повторений (rep) 5000000000.

Тело Lt1_tst:
Код:
  mov      rcx,  [rep]
@loop:
  comisd   xmm0, [c]
  setb     al
  sub      rcx,  1
  jnz      @loop

Тело Lt2_tst:
Код:
  mov      rcx,   [rep]
@loop:
  movsd    xmm0, [d]
  movsd    xmm1, [c]
  cmpsd    xmm0, xmm1, 001b
  movmskpd eax,  xmm0
  sub      rcx,  1
  jnz      @loop

Тело Eq_tst:
Код:
  mov      rcx,  [rep]
@loop:
  movsd    xmm0, [d]
  movq     rax,  xmm0
  xor      rax,  [c]
  setz     al
  sub      rcx,  1
  jnz      @loop
Т.е. тела функций вставлены в одно и то же "окружение".

На Sandy Bridge время выполнения Lt1_tst равно приблизительно $0.76\pm 0.02$ времени выполнения Eq_tst, а время выполнения Lt2_tst было приблизительно равно времени выполнения Eq_tst, как при значении аргумента или $c$ порядка 1, так и при денормализованном значении.
В общем, ускорить сравнение на равенство для денормализованных при помощи xor не получилось.

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 15:56 
Заслуженный участник


12/07/07
4522
Виноват. В Lt1_tst не вставил movsd xmm0, [d]. После вставки скорости выполнения всех трех процедур оказались приблизительно равными.

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 17:37 
Заслуженный участник


20/08/14
11766
Россия, Москва
Как-то эти сравнения немного странные: для подсчёта теоретических цифр команду загрузки лучше бы отделить от команды сравнения (по причине неуказания Фогом latency для операций с операндом в памяти) и вообще вынести за пределы сравнения, получим такой код (метки лишь для идентификации кусков кода, не для переходов) (Sandy Bridge):
Код:
        movsd           xmm1,[c]        ;3/0.5
Lt1:    comisd          xmm0,xmm1       ;2/1
        setb            AL              ;1/0.5
        ;                               =3/1.5

        movsd           xmm1,[c]        ;3/0.5
Lt2:    cmpsd           xmm0,xmm1,001b  ;3/1
        movmskpd        EAX,xmm0        ;2/1
        ;                               =5/2

        mov             RBX,[c]         ;2/0.5
Eq:     movq            RAX,xmm0        ;1/1
        xor             RAX,RBX         ;1/0.33
        setz            AL              ;1/0.5
        ;                               =3/1.83
Кроме того, Вы в своих тестовых процедурах померили throughput, а для условного перехода после сравнения важна latency, которая как видно отличается в полтора раза. Для приближения тестов к реальности надо было сделать сразу несколько вещей: сравнивать с двумя (или более, смотря какое распределение вероятностей переходов интересует) разными значениями, а не с одним; добавить зависимость по данным (вычисленному результату сравнения); для уменьшения влияния обрамления продублировать код сравнения хоть пару десятков (а лучше - сотню) раз с сохранением сквозной зависимости по данным; возможно внести обратно загрузку константы если сравнения используются с разными величинами.

Ещё момент: такие короткие функции обычно объявляются inline и подставляются прямо в код, а в этом случае желательнее результат сравнения получать именно в флагах для последующего условного перехода и команда setcc оказывается лишней - и тогда Lt1 и Eq уменьшаются, а и так долгая Lt2 наоборот увеличивается (на команду сравнения или логического and с константой) и времена различаются ещё сильнее: 2/1 и 2/1.33 против 6/2.33 - уже аж втрое. В реальном коде команды сравнений могут быть и такими (метки тоже лишь для удобства):
Код:
Lt1:    comisd          xmm0,[c]        ;2/1
        jb

Lt2:    cmpsd           xmm0,[c],001b   ;3/1
        movmskpd        EAX,xmm0        ;2/1
        and             EAX,0x01        ;1/0.33
        jz

Eq:     movq            RAX,xmm0        ;1/1
        xor             RAX,[c]         ;1/0.5 - из-за throughput чтения памяти
        jz

Но вообще оптимизировать скорость таких маленьких кусков кода смысла мало, практически всегда любые различия в его скорости многократно перекроются различиями в скорости прочего кода, плюс зависимость от заполнения конвейеров и кэшей. В качестве упражнения можно, на практике - бессмысленно.

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 18:47 
Аватара пользователя


14/11/12
1367
Россия, Нижний Новгород
Возможно на некоторых процессорах экономия двух лишних if окажется выгоднее чем одно дополнительное умножение:

Используется синтаксис C
static inline bool is_quasi_equal (double x, double y) {
    register const double d = x - y;
    return (d * d) < (DBL_EPSILON * DBL_EPSILON);
}
 

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 20:25 
Заслуженный участник


12/07/07
4522
Dmitriy40
Если вынести копирование аргумента в xmm0 за цикл (movsd xmm0, [d]), то в Lt2_tst cmpsd будет генерировать недопустимую операцию, как только в младшей части xmm0 будет получена Истина (все единицы).

Если вынести за цикл movsd xmm1 [c] (но оставить в присвоение al), то самой быстрой будет Lt2_tst (почти в два раза быстрее Eq_tst, а Lt1_tst будет совсем немного быстрее Eq_tst).

Если перед циклом вставить
Код:
pxor  xmm2, xmm2
movsd xmm2, [d]
, а внутри цикла movaps xmm0, xmm1 и убрать присвоение al в Lt1_tst (setb al) и Eq (setz al), но оставить в Lt2_tst (movmskpd eax, xmm0), то времена выполнения всех трех процедур будут приблизительно равны (в пределах погрешности). Возможно Lt2_tst чуть медленней, не было времени более старательно протестировать. Выполнялось по 3-4 запуска.

В целом, конечно, это всё нереалистично. Если не вызывать функцию, то загрузки можно сделать с некоторым упреждением и думать в каком виде возвращать результат: во флагах или сохранять в переменную. И уровень тестирования не соответствующий, и… Ну, понятно.

Да, при компиляции c текста на языке высокого уровня XE7 использует comisd.

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение27.09.2018, 21:54 
Заслуженный участник


20/08/14
11766
Россия, Москва
GAA в сообщении #1341985 писал(а):
Если вынести за цикл movsd xmm1 [c] (но оставить в присвоение al), то самой быстрой будет Lt2_tst (почти в два раза быстрее Eq_tst,
Простите, но это чудеса огрехи тестирования: команды cmpsd,movmskpd в Lt2 ложатся в первый и нулевой порты и могут выполниться за 1 такт; команды же comisd,setb в Lt1 и movq,xor,setz в Eq вполне могут быть распределены в два/три разных порта и выполниться тоже за один такт. Добавка чтения из памяти даже двух чисел влиять не должна, они распределятся в 2 и 3 порты и дополнительного времени не займут. Т.е. Lt2 никак не может быть вдвое быстрее. Это в теории. Как будет на практике - зависит от окружения (остального кода в потоке выполнения).
Анализ (Intel Architecture Code Analyzer) подтверждает мою мысль: все три тела сравнений за 1 такт.

(Логи анализа)

Сначала общая часть, потом в порядке Lt1, Lt2 (с AVX, намеренно, см. ниже), Eq:
Код:
Intel(R) Architecture Code Analyzer Version - 2.1
Analyzed File - xmm_test.bin
Binary Format - 64Bit
Architecture  - SNB
Analysis Type - Throughput

N - port number or number of cycles resource conflict caused delay, DV - Divider pipe (on port 0)
D - Data fetch pipe (on ports 2 and 3), CP - on a critical path
F - Macro Fusion with the previous instruction occurred
* - instruction micro-ops not bound to a port
^ - Micro Fusion happened
# - ESP Tracking sync uop was issued
@ - SSE instruction followed an AVX256 instruction, dozens of cycles penalty is expected
! - instruction not supported, was not accounted in Analysis

Throughput Analysis Report
--------------------------
Block Throughput: 1.00 Cycles       Throughput Bottleneck: Port0, Port1, Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.0  |
-------------------------------------------------------------------------

| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   2    | 1.0       | 1.0 |           |           |     |     | CP | comisd xmm0, xmm1
|   1    |           |     |           |           |     | 1.0 | CP | setb al
Total Num Of Uops: 3

Throughput Analysis Report
--------------------------
Block Throughput: 1.00 Cycles       Throughput Bottleneck: Port0, Port1

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 0.0  |
-------------------------------------------------------------------------

| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    |           | 1.0 |           |           |     |     | CP | vcmpsd xmm2, xmm0, xmm1, 0x1
|   1    | 1.0       |     |           |           |     |     | CP | vmovmskpd eax, xmm2
Total Num Of Uops: 2

Throughput Analysis Report
--------------------------
Block Throughput: 1.00 Cycles       Throughput Bottleneck: Port0, Port1, Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.0  |
-------------------------------------------------------------------------

| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    | 1.0       |     |           |           |     |     | CP | movq rax, xmm0
|   1    |           | 1.0 |           |           |     |     | CP | xor rax, rbx
|   1    |           |     |           |           |     | 1.0 | CP | setz al
Total Num Of Uops: 3
Что интересно, если вернуться к Вашему варинту с SSE кодом, то появляется зависимость между итерациями в Lt2 и она занимает уже 3 такта вместо одного:
Код:
Throughput Analysis Report
--------------------------
Block Throughput: 3.00 Cycles       Throughput Bottleneck: InterIteration

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 0.0  |
-------------------------------------------------------------------------

| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    |           | 1.0 |           |           |     |     | CP | cmpsd_xmm xmm0, xmm1, 0x1
|   1    | 1.0       |     |           |           |     |     | CP | movmskpd eax, xmm0
Total Num Of Uops: 2
Как Вы натестировали Lt2 вдвое быстрее остальных не представляю, но вполне допускаю такую возможность. Но это именно недостаток методики тестирования, к реальной скорости относящийся мало.

GAA в сообщении #1341985 писал(а):
убрать присвоение al в Lt_1 (setb al) и Eq (setz al), но оставить в Lt_2 (movmskpd eax, xmm0),
Простите, но этого недостаточно, movmskpd не изменит флаги, в отличии от comisd и xor, а значит после неё надо добавлять команду and (или аналогичную) как у меня в втором варианте кода в предыдущем сообщении. И Lt2 ещё замедлится (в плане latency).

PS. Сам я к сожалению не могу запустить адекватный тест - у меня Haswell, а в нём 4 порта запуска и времена достаточно другие, разбираться в степени этого влияния на скорость пары-тройки отдельных команд я не вижу совершенно никакого смысла. Как и вообще в оптимизации этих команд в реальной программе.

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение29.09.2018, 17:17 
Заслуженный участник


12/07/07
4522
Я не добавил and поскольку тенденция была видна и тест не для этого. Если не использовать функцию (proc), то ответ заранее был ясен. Тестил именно на сохранение результата в регистре общего назначения. Помимо возвращаемого результата функции, сохранение результата сравнения в регистре общего назначения может понадобиться для получения смещения (для чтения данных из памяти) или для временного сохранения результата для последующего использования внутри функции. Именно сохранение результата в регистре меня и интересовало.
В предыдущем варианте неудачно вставил movaps. Это и неаккуратность тестирования дало такой удивительный эффект. Немного улучшенный вариант тестирования с movaps: 10 раз повторяется основной блок.

Delphi. Для старых версий (до RAD Studio XE2) нужно 64 битные регистры заменить на 32 битные регистры. О баге в XE2 - XE6 писал выше и в других темах раньше.)
код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
const
 rep: int64  = 5000000000;

function Lt1_tst: Boolean;
asm
  mov      rcx,  [rep]
  movsd    xmm0, [d]
  movsd    xmm1, [c]
  movaps   xmm2,  xmm0

 @loop:
  comisd   xmm0, xmm1
  setb     al
  movaps   xmm0, xmm2

  comisd   xmm0, xmm1
  setb     al
  movaps   xmm0, xmm2

  ...

  sub      rcx,  1
  jnz      @loop
end;

function Lt2_tst: Boolean;
asm
  mov      rcx,   [rep]
  movsd    xmm0,  [d]
  movsd    xmm1,  [c]
  movaps   xmm2,  xmm0

@loop:
  cmpsd    xmm0, xmm1, 001b
  movmskpd eax,  xmm0
  movaps   xmm0, xmm2

  cmpsd    xmm0, xmm1, 001b
  movmskpd eax,  xmm0
  movaps   xmm0, xmm2

  ...

  sub      rcx,  1
  jnz      @loop
end;


function Eq_tst: Boolean;
asm
  mov      rcx,  [rep]
  movsd    xmm0, [d]
  mov      r9,   [c]
  movaps   xmm2,  xmm0

@loop:
  movq     rax,  xmm0
  xor      rax,  r9
  setz     al
  movaps   xmm0, xmm2

  movq     rax,  xmm0
  xor      rax,  r9
  setz     al
  movaps   xmm0, xmm2

  ...

  sub      rcx,  1
  jnz      @loop
end;

var
 Time1, Time2, d1, d2, d3: TDateTime;
 t: text;
begin
  Time1:= Time;
   Lt1_tst;
  Time2:= Time;
  d1:= Time2-Time1;

  Time1:= Time;
   Lt2_tst;
  Time2:= Time;
  d2:= Time2-Time1;

  Time1:= Time;
   Eq_tst;
  Time2:= Time;
  d3:= Time2-Time1;

  Assign(t, 'cmp1.txt');
{$I-}
  Append(t);
  if IOResult <> 0 then Rewrite(t);
{$I+}
  Writeln(t, '   Rep=', rep);
  Writeln(t,  'd1/d2=', d1/d2:7:4, '  d3/d2', d3/d2:7:4);
  Close(t);
end.
Результаты для пяти запусков и средние значения
Код:
d1/d2   d3/d2
1.4581  1.5002
1.3568  1.4384
1.4928  1.5622
1.4306  1.4487
1.4771  1.4655
------------------
1.44     1.48

Формальная замена movaps на pxor заметно изменяет результат в сторону ожидаемого.
Код:
d1/d2      d3/d2
1.1302     1.1370
1.0799     1.1735
1.0609     1.1188
1.0437     1.1419
1.0833     1.1138
------------------
1.08       1.14

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение29.09.2018, 18:39 
Заслуженный участник


20/08/14
11766
Россия, Москва
А почему movaps если везде используются double? На работу это конечно не влияет, но нелогично.

Вот тут, после добавления в цикл команды movapd, и проявляется во всей красе зависимость от окружающего кода: Lt2 остаётся 1.0 такта, а и Lt1 и Eq становятся 1.5 такта - им не хватает трёх портов запуска для 4-х микроопераций за такт, в Lt2 же 3 микрооперации укладываются в 3 порта запуска.

(Снова логи IACA)

Код:
Intel(R) Architecture Code Analyzer Version - 2.1
Analyzed File - xmm_test.bin
Binary Format - 64Bit
Architecture  - SNB
Analysis Type - Throughput

*******************************************************************
Intel(R) Architecture Code Analyzer Mark Number 1
*******************************************************************

Throughput Analysis Report
--------------------------
Block Throughput: 1.55 Cycles       Throughput Bottleneck: Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.5    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.5  |
-------------------------------------------------------------------------


| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   2    | 1.0       | 1.0 |           |           |     |     |    | comisd xmm0, xmm1
|   1    | 0.4       |     |           |           |     | 0.6 | CP | setb al
|   1    |           |     |           |           |     | 1.0 | CP | movapd xmm0, xmm2
Total Num Of Uops: 4

*******************************************************************
Intel(R) Architecture Code Analyzer Mark Number 2
*******************************************************************

Throughput Analysis Report
--------------------------
Block Throughput: 1.00 Cycles       Throughput Bottleneck: Port0, Port1, Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.0  |
-------------------------------------------------------------------------


| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    |           | 1.0 |           |           |     |     | CP | cmpsd_xmm xmm0, xmm1, 0x1
|   1    | 1.0       |     |           |           |     |     | CP | movmskpd eax, xmm0
|   1    |           |     |           |           |     | 1.0 | CP | movapd xmm0, xmm2
Total Num Of Uops: 3

*******************************************************************
Intel(R) Architecture Code Analyzer Mark Number 3
*******************************************************************

Throughput Analysis Report
--------------------------
Block Throughput: 1.55 Cycles       Throughput Bottleneck: Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.5    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.5  |
-------------------------------------------------------------------------


| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    | 1.0       |     |           |           |     |     |    | movq rax, xmm0
|   1    |           | 1.0 |           |           |     |     |    | xor rax, rbx
|   1    | 0.4       |     |           |           |     | 0.6 | CP | setz al
|   1    |           |     |           |           |     | 1.0 | CP | movapd xmm0, xmm2
Total Num Of Uops: 4
И именно эти цифры Вы и намерили в первом варианте своих процедур (с учётом погрешностей и 1.44 и 1.48 близки к 1.5).

GAA в сообщении #1342357 писал(а):
Формальная замена movaps на pxor заметно изменяет результат в сторону ожидаемого.
Если имеется в виду замена на pxor xmm0,xmm0, то она вовсе не формальная, такое обнуление регистров не распределяется в порты запуска и времени не занимает, т.е. в смысле скорости эквивалентна исключению команды movaps, а не замене её на другую. И все тела сравнений обратно становятся однотактовыми.

(Подтверждающий лог IACA для Eq)

Код:
Throughput Analysis Report
--------------------------
Block Throughput: 1.00 Cycles       Throughput Bottleneck: FrontEnd, Port0, Port1, Port5

Port Binding In Cycles Per Iteration:
-------------------------------------------------------------------------
|  Port  |  0   -  DV  |  1   |  2   -  D   |  3   -  D   |  4   |  5   |
-------------------------------------------------------------------------
| Cycles | 1.0    0.0  | 1.0  | 0.0    0.0  | 0.0    0.0  | 0.0  | 1.0  |
-------------------------------------------------------------------------


| Num Of |              Ports pressure in cycles               |    |
|  Uops  |  0  - DV  |  1  |  2  -  D  |  3  -  D  |  4  |  5  |    |
---------------------------------------------------------------------
|   1    | 1.0       |     |           |           |     |     | CP | movq rax, xmm0
|   1    |           | 1.0 |           |           |     |     | CP | xor rax, rbx
|   1    |           |     |           |           |     | 1.0 | CP | setz al
|   0*   |           |     |           |           |     |     |    | pxor xmm0, xmm0
Total Num Of Uops: 3
И тут тоже Ваши измерения подтверждают теорию: все времена у Вас с неплохой точностью устремились к общей скорости (1.0 такту).

Продублировав код 2-3 сотни раз можно получить и две правильные цифры после запятой, особенно если полное время выполнения каждой процедуры занимает хотя бы десяток секунд (чтобы нивелировать с той же точностью вмешательство ОС и погрешность тиков таймера). Зачем - не знаю, уже достигнутой Вами точности для анализа достаточно.

Вот что даёт даже небольшое исправление методики тестирования в правильную сторону, сразу теория и практика почти сходятся. :D

 Профиль  
                  
 
 Re: Помогите ускорить функцию на С
Сообщение29.09.2018, 19:32 
Заслуженный участник


12/07/07
4522
Первоначально писал на автомате (не было времени), а если обдумывать нет времени, то пользуюсь общими рекомендациями (см., например, “Intel 64 and IA-32 architectures optimization reference manual”, 2015, но можно и за другие годы.)
“When floating-point operations are bitwise equivalent, use PS data type instead PD data type. MUVAPS and MUVAPD do the same thing, but MOVAPS takes one less byte to encoding the instruction.”
“Partial registers stalls can also apply to XMM registers. The following SSE and SSE2 instruction update only part of the destination register:
MUVL/HPD XMM, MEM64
MUVL/HPD XMM, MEM32
MOVSS/SD between registers
Using these instructions create a dependency chain between the unmodified part of the register and the modified part of the register. This dependency chain can cause performance loss.”

Замена movaps на xor формальная, поскольку полезного действия теперь блок не выполняет. Да, как Вы и пишете, исходил из того, что мопы «обнуляющих» (xor, sub, pcmpg) и задающих константу «единицы» (pcmpeg) инструкций планировщик (SC, scheduler) помечает как незапускаемые. Поэтому замена должна была продемонстрировать влияние добавления movaps. [Тестил и писал не на своём компе, состояние процессора на этапе выполнения не отслеживалось. А потому тесты особо ни на что не претендуют, но вроде показательные.]

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 26 ]  На страницу Пред.  1, 2

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group