Ошибки, баги, вопросы - страница 2160

 

Сообщение об ошибке не позволяет понять причину в обширном коде

struct A {
    void f( int    ) {}
    void g( int fn ) { f( fn ); } //Error: ')' - unexpected token ???
};
typedef void (*fn)();

ниже понятно

struct A {
    void f( int    ) {}
    void g( int fn ) { f( fn ); } //Error: 'fn' - identifier already used
};
struct fn {};
 

Нет сообщения об ошибке

typedef void (*fn)();
void f( int i ) { Print( __FUNCTION__, ":", i ); }
void OnStart()
{
    f( fn() );
}

более того - выполняется и даже результат есть (!)

В таком варианте:

typedef int (*fn)();
void OnStart() { Print( fn() ); }

не работает переход к строке ошибки (по Enter)


 

Ошибка при компиляции

    void g(  int ) {}
    void g( uint ) {}
#import "Test.ex5"
    void f(  int );
    void f( uint );
#import
typedef void (*fn1)(  int );
typedef void (*fn2)( uint );
void OnStart()
{
    fn1 g1 = g; /*нормально и результат*/ g1( 1 ); //совпадает с ожидаемым
    fn2 g2 = g; /*нормально и результат*/ g2( 1 ); //совпадает с ожидаемым
    fn1 f1 = f; //Error: 'f' - cannot resolve function address
    fn2 f2 = f; /*нормально и результат*/ f2( 1 ); //совпадает с ожидаемым
                                                   //при наличии Test.ex5
}
 

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

И сейчас, когда я попытался разобраться с этой странностью, запутался еще больше. 

Итак, в расчете я использую функцию квадратного корня sqrt(), которую я решил обойти путем создания double массива.

Т.к. я беру квадратный корень всегда с целых чисел, то создание массива выглядит так:

   double SQRT[];
   int ss=Width*Width+Height*Height;
   ArrayResize(SQRT,ss);
   for(double w=0;w<ss;w++) SQRT[(int)w]=sqrt(w);

Логично предположить, что считывание из массива SQRT[x] выполняется быстрее, чем функция sqrt(x).
И это подтверждается проверочным кодом:

   double Sum1=0,Sum2=0;
   ulong t=GetMicrosecondCount();
   for(int i=0;i<ss;i++) Sum1+=sqrt(double(i));   // Находим сумму квадратных корней всех целых чисел от 0 до ss
   t=GetMicrosecondCount()-t;
   Print("Время на сумму "+IntegerToString(ss)+" значений функций = "+IntegerToString(t)+" мкс, Сумма = "+DoubleToString(Sum1));
   t=GetMicrosecondCount();
   for(int i=0;i<ss;i++) Sum2+=SQRT[(int)(i)];    // Находим сумму квадратных корней всех целых чисел от 0 до ss через сложение элементов массива SQRT[]
   t=GetMicrosecondCount()-t;
   Print("Время на сумму "+IntegerToString(ss)+" значений массива = "+IntegerToString(t)+" мкс, Сумма = "+DoubleToString(Sum2));

результат свидетельствует о выигрыше скорости ~ 1.5 раза:

Но когда я заменяю в коде функцию sqrt() на считывание элементов массива SQRT[], вместо ускорения работы, я получаю жуткие тормоза.

Считывание элемента из массиваа SQRT[] происходит в разы, может быть даже на порядок дольше, чем выполнение функции sqrt().

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

Можно предположить, что компилятор каким-то странным образом осуществляет доступ к большому массиву и на каждом витке цикла словно "забывает" о массиве и каждый раз проводит свою какую-то служебную индексацию. 

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

Прилагаю код скрипта для демонстрации этой странности.

При запуске по умолчанию (arr=false) вычисляются функции sqrt(), а при изменении arr на true значение квадратного корня берется из массива.

ЧТО НЕ ТАК? ОТКУДА ТОРМОЗА?

Файлы:
Swirl.mq5  11 kb
 
Nikolai Semko:

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


Логично предположить, что считывание из массива SQRT[x] выполняется быстрее, чем функция sqrt(x).

Из динамического массива не факт.

Тем более, что взятие корня давно уже в есть на уровне команды процессора SQRTSD. То есть, с помощью заведомых расходов на доступ в динамическом массиве, выиграть у процессорной реализации уже не получится.


И это подтверждается проверочным кодом:

результат свидетельствует о выигрыше скорости ~ 1.5 раза:

Сомневаюсь, что подтверждается.

Код такого вида прям как из учебника по оптимизации циклов. Оптимизатор кода из него просто конфетку может сделать, так как случай очень простой:

   for(int i=0;i<ss;i++) 
      Sum2+=SQRT[i];

То есть, замер производительности в случае применения заведомо идеально оптимизируемых случаев нужно четко соотносить с целями теста.

В данном случае тест неправильный.

Считывание элемента из массиваа SQRT[] происходит в разы, может быть даже на порядок дольше, чем выполнение функции sqrt().

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

Не видя финального ассемблерного кода, я склоняюсь к:

  1. sqrt на самом деле быстро работает (у нас очень эффективный генератор в нативный код, sqrt превращается в чистый SQRTSD)
  2. проверочный код слишком прост и компенсирует задержки оптимизацией


Код весь проверим внимательно. Интересно найти, в чем реальная причина.

SQRTSD — Compute Square Root of Scalar Double-Precision Floating-Point Value
  • www.felixcloutier.com
Computes square root of the low double-precision floating-point value in xmm3/m64 and stores the results in xmm1 under writemask k1. Also, upper double-precision floating-point value (bits[127:64]) from xmm2 is copied to xmm1[127:64].
 
Renat Fatkhullin:

Из динамического массива не факт.


Не видя финального ассемблерного кода, я склоняюсь к:

  1. sqrt на самом деле быстро работает (у нас очень эффективный генератор в нативный код, sqrt превращается в чистый SQRTSD)
  2. проверочный код слишком прост и компенсирует задержки оптимизацией

Пробовал статический массив - тоже самое. 

Как бы sqrt не был бы быстр, но мне сложно поверить в то, что эта функция может быть в 10 раз быть быстрее простого чтения элемента массива.

Причем, если в моем примере уменьшить размер канваса, скажем 50х50 ( для этого существует входной параметр Size, нужно вместо 0 установить 50), 

и массив будет значительно меньше (5000 элементов), то скоростная картина заметно меняется. Нет уже такого сильного контраста.

Но мне не понятно, разве от размера массива зависит скорость доступа к его элементам?

А насчет простоты проверочного кода, пожалуй, Вы правы.

Попробовал чуть усложнить и результат совсем другой:

for(int i=0;i<ss;i++) Sum1+=i*sqrt(double(i));
....
for(int i=0;i<ss;i++) Sum2+=i*SQRT[(int)(i)];


 
Renat Fatkhullin:


Код весь проверим внимательно. Интересно найти, в чем реальная причина.

Круто! Спасибо! Мне тоже интересно.

А sqrt, действительно, очень быстр. Меньше наносекунды :)). Фантастика!

 
Nikolai Semko:

Пробовал статический массив - тоже самое. 

Как бы sqrt не был бы быстр, но мне сложно поверить в то, что эта функция может быть в 10 раз быть быстрее простого чтения элемента массива.

На текущий момент множество старых оптимизационных техник уже не работают.


Причем, если в моем примере уменьшить размер канваса, скажем 50х50 ( для этого существует входной параметр Size, нужно вместо 0 установить 50), 

и массив будет значительно меньше (5000 элементов), то скоростная картина заметно меняется. Нет уже такого сильного контраста.

Но мне не понятно, разве от размера массива зависит скорость доступа к его элементам?

Нет чистых доказательств ваших расчетов, вы делаете предположения на основе очень замусоренного кода.

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

 
Renat Fatkhullin:


Нет чистых доказательств ваших расчетов, вы делаете предположения на основе очень замусоренного кода.

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

Я лишь вижу, что просто поменяв sqrt на простое чтение из массива, общая скорость расчета падает в 4 раза. А общая доля sqrt в этом достаточно большом расчете каждого пикселя вряд ли превышает 10%, а то и нескольких %.

Явная аномалия!

Я даже ошибся сказав, что время доступа к элементу массива больше в 10 раз времени sqrt. Гораздо больше с учетом того, что sqrt так быстр, а общее замедление так велико.

 
Nikolai Semko:

Я лишь вижу, что просто поменяв sqrt на простое чтение из массива, общая скорость расчета падает в 4 раза. А общая доля sqrt в этом достаточно большом расчете каждого пикселя вряд ли превышает 10%, а то и нескольких %.

Явная аномалия!

Я объяснил почему. Оптимизации математики девяностых годов уже не работают.

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

На таком уровне технической [не]чистоты я не буду обсуждать вопросы.