Перейти к содержанию
    

С++ лямбды и многомерные Variable Sized Arrays

1 hour ago, Kabdim said:

А с другой стороны интеловый фортран компилятор позволяет приблизится по эффективности к программе на С.

Наоборот - это С приближается к Фортрану (как не странно). Дело в том, что в модели вызовов функций в Фортране есть один замечательный момент. Постулируется, что все параметры независимы, т.е. оптимизатор имеет право считать, что никакие переданные массивы никогда не пересекаются. В С все массивы передаются указателями, и оптимизатор обязан считать наоборот - что любые параметры/указатели могут пересечься. А так как вся математика крутится вокруг массивов, то это сразу отрубает добрую половину возможных оптимизаций (причём в очень критичном месте).

Компиляторы сейчас достаточно умные, что бы понять где и что пересекается, а где нет, но всё равно отследить до конца они не в состоянии (не зря в С появилось ключевое слово restrict)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо всем за интересные советы и обсуждения!

 

Так все-таки вдруг кто знает, скажите, пожалуйста, есть ли возможность отказаться от строчки

double (*X)[M] = static_cast<double (*)[M]>((void*)_X);

в каждой лямбде, описав ее один раз на функцию и желательно по стандарту и без использования std::vector ?

 

Спасибо!

 

ИИВ

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 часов назад, xvr сказал:

Наоборот - это С приближается к Фортрану (как не странно). .. Постулируется, что все параметры независимы ... (не зря в С появилось ключевое слово restrict)

 

Вы уж определитесь только приближается или есть restrict, которым нужно пользоваться/использовать правильно написанные библиотеки. Но в целом конечно да, фортран для ученых требует меньших чем для cpp ментальных усилий при приличном результате.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 hours ago, iiv said:

Func3 - и только тут с копией в каждой единице функции удается все скомпилировать.

Я потыкал Ваш код на godbolt.org - только gcc и умеет его компилировать. Все остальные ругаются на трюки с приведением типов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Kabdim said:

Вы уж определитесь только приближается или есть restrict, которым нужно пользоваться/использовать правильно написанные библиотеки.

Во первых restrict появился сравнительно недавно (сравнительно с возрастом Фортрана)

Во вторых им почти не пользуются :(

Ну и если им пользоваться (и/или использовать правильные библиотеки), то разницы в качестве оптимизации не будет :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 часов назад, xvr сказал:

Во первых restrict появился сравнительно недавно (сравнительно с возрастом Фортрана)

Во вторых им почти не пользуются :(

Ну и если им пользоваться (и/или использовать правильные библиотеки), то разницы в качестве оптимизации не будет :)

Он больше не на качество оптимизации влияет, а скорее на правильность результата после применения оптимизации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/4/2019 at 4:30 PM, iiv said:

Спасибо! Лет десять у нас была такая идея :) там ключевая проблема в том, что компилер начинает сходить с ума, раскрывая все инлайны и код становится огромным и не влазит в кеш инструкций, соответсвенно тут же падает производительность.

ну тогда могу предложить ещё более замечательные костыли: вставить код внутренней функции один раз как есть, а вызовы этой функции заменить на метки и goto, и прыгать туда сюда, jmp, call, какая разница :), ну переменную ещё одну завести придётся, счётчик вызовов и switch в конце функции добавить, чтобы возвращаться из этой "локальной" функции туда откуда вызывали. ну и локальные переменные под аргументы и возвращаемое значение.

void Func(int N, int M, double X[N][M]){
  int i, j;

  int Test_call_cnt = 0;
  goto Test_end;
  :Test
{ int i, j; for(i=0; i<N; i++) for(j=0; j<M; j++) X[i][j]=(double)(i*1000+j); 
  
  Test_call_cnt += 1;
  switch (Test_call_cnt){
  case 1 : goto Test_call_1;
  case 2 : goto Test_call_2;
  case 3 : goto Test_call_3;
  }
}
  :Test_end


  goto Test;
  :Test_call_1

  for(i=0; i<N; i++)
  { double s=0;
    for(j=0; j<M; j++)
      s+=X[i][j]*X[i][j];
    s=(s>0.)?1./sqrt(s):0.;
    for(j=0; j<M; j++)
      X[i][j]*=s;
  }

  goto Test;
  :Test_call_2

  return;
}

а ещё в макросы всю эту красоту обернуть :) чтобы не так страшно было.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 04.02.2019 в 18:30, iiv сказал:

Но оно точно потянет перетестирование всей оптимизации кеша и мультитредингов (вложенные функции ужасно удобны в ручном мультитрединге).

Если включить full LTO, с точки зрения компилятора ничего поменяться не должно кроме удобства для программиста. Разбивать на маленькие исходники вполне можно научится скриптом. Честно говоря не вижу в этой задаче человекогоды.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

40 minutes ago, Kabdim said:

не вижу в этой задаче человекогоды

Ну, скажем, код __pv вполне может стоить человекожизни. Потому что искать случайную опечатку в этой красоте - убицца просто.

 

Имхо - оставить всё как есть или медленно и печально переносить на std::vector или какой-нибудь самодельный vector2d.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, esaulenka said:

Ну, скажем, код __pv вполне может стоить человекожизни. Потому что искать случайную опечатку в этой красоте - убицца просто. 

 

Имхо - оставить всё как есть или медленно и печально переносить на std::vector или какой-нибудь самодельный vector2d. 

Спасибо большое всем за интересные советы!

 

Действительно, на многих не поддерживается даже то, что у меня в func3. На удивление последний интел это стал поддерживать, я принял это за чистую монету и поэтому кинулся в этом направлении.

 

Похоже да, если и переползать на С++, то пользовать что-то самодельное похожее на std::vector.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 минут назад, esaulenka сказал:

Ну, скажем, код __pv вполне может стоить человекожизни.

Но ведь я говорил не про код __pv... Там цитата про обсуждение разделения кода на файлы. У __pv ключевая фраза про оборачивание страшного кода в макросы сама по себе достаточно знаковая.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

40 minutes ago, esaulenka said:

Ну, скажем, код __pv вполне может стоить человекожизни. Потому что искать случайную опечатку в этой красоте - убицца просто.

я старался.

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

и в результате наверное можно добиться чего-нибудь вроде

void Func(){

DECLARE_LOCAL_FUNC (Test){
...
}
...
CALL(Test);
...
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 hours ago, Arlleex said:

Он больше не на качество оптимизации влияет, а скорее на правильность результата после применения оптимизации.

Нет, именно на качество. Если после оптимизации результат не правильный, то оптимизатор не вправе эту оптимизацию применять. А restrict развязывает оптимизатору руки, и он может применить более агрессивные оптимизации.

 

PS. Я не утверждаю, что оптимизатор никогда не создаёт неправильного кода - ошибки у всех бывают, в том числе и у писателей компиляторов :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 04.02.2019 в 18:45, iiv сказал:

Добрый день,

Попробовал в лоб сделать на ламбдах, получилось так:


void Func3(int N, int M, double *_X)
{ double (*X)[M] = static_cast<double (*)[M]>((void*)_X);

  auto Test = [&] () -> void
  { int i, j;
    double (*X)[M] = static_cast<double (*)[M]>((void*)_X);
    for(i=0; i<N; i++)
      for(j=0; j<M; j++)
        X[i][j]=(double)(i*1000+j); // теперь он все видит, но приходится писать double (*X)[M] ... в каждой лямбде и копировать указатель из _Х
  };

..
  
}

 

Если проблема только в том, что надо чем-то заменить массивы переменной длины, то можно вместо проблемного static_cast соорудить какой-нибудь класс, типа

FlexibleArray2d, и вместо кастования создавать экземпляр этого класса. Его и использовать в лямбде (потыкать на wandbox-е):

// одномерный массив переменной размерности
template<typename T>
struct FlexibleArray
{
    FlexibleArray(size_t xDim, T* rawData) : x(xDim), data(rawData) {}
    T& operator[](size_t xPos) { return data[xPos]; }
private:
    const size_t x;
    T* data;
};

// двумерный массив переменной размерности
template<typename T>
struct FlexibleArray2d
{
FlexibleArray2d(size_t xDim, size_t yDim, T* rawData)
    : x(xDim)
    , y(yDim)
    , data(rawData)
    {}
    FlexibleArray<T> operator[](size_t yPos) { return FlexibleArray<T>(x, &data[yPos * x]); }
private:
   const size_t x;
   const size_t y;
   T* data;
};


// использование
void Func3(int N, int M, double *_X)
{
    // вместо static_cast-а делаем вот как:
    FlexibleArray2d<double> X(N, M, _X);

    // и далее по тексту
  auto Test = [&] () -> void
  { int i, j;
    for(i=0; i<N; i++)
      for(j=0; j<M; j++)
        X[i][j]=(i*1000+j);
  };

  int i, j;
  Test();

  for(i=0; i<N; i++)
  { double s=0;
    for(j=0; j<M; j++)
      s+=X[i][j]*X[i][j];
    s=(s>0.)?1./sqrt(s):0.;
    for(j=0; j<M; j++)
      X[i][j]*=s;
  }
  return;
}


В качестве бонуса можно добавить проверки на выход за пределы диапазонов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 hours ago, _pv said:

ну тогда могу предложить ещё более замечательные костыли: вставить код внутренней функции один раз как есть, а вызовы этой функции заменить на метки и goto, и прыгать туда сюда, jmp, call, какая разница :), ну переменную ещё одну завести придётся, счётчик вызовов и switch в конце функции добавить, чтобы возвращаться из этой "локальной" функции туда откуда вызывали. ну и локальные переменные под аргументы и возвращаемое значение.

. . . .  а ещё в макросы всю эту красоту обернуть :) чтобы не так страшно было.

Это получится, насколько понял, Protothreads http://dunkels.com/adam/pt/   ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...