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

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

34 minutes ago, k155la3 said:

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

ну не совсем, там в макросах прячется условный goto, чтобы внутрь бесконечного цикла в нужное место при очередном вызове сразу запрыгивать, делая вид что просто продолжили с того места откуда в прошлый раз return был,

тут же надо просто один и тот же кусок кода оформленный в "локальную" функцию несколько раз позвать, но да, похоже наверное.

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


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

Во-первых, огромное спасибо всем за интересные советы!

 

Лямбды (вложенные функции) раскрывать в goto - не реально, они как раз параллелятся, по ним часто мультитред делается, то есть их выполнение зависит от числа ядер или тредов.

 

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

#include <math.h>

template <typename T> class MDA2
{ public:
  T *A;
  int LDA;
  MDA2() {};
  MDA2(T *aA, int aLDA) { A=aA; LDA=aLDA; };
  void Init(T *aA, int aLDA) { A=aA; LDA=aLDA; return; };
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};


void Func3(int N, int M, double *_X)
{ MDA2<double> X(_X, N);

  auto Test = [&] () -> void
  { int i, j;
    for(i=0; i<N; i++)
      for(j=0; j<M; j++)
        X(i,j)=(double)(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;
}

Конечно надобно на все размерности 2,3,4,... написать такие темплейты, но по крайней мере

1. пример компилится на всех адекватных и свежих компилерах включая интел, гну и вижуал

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

 

Из пожеланий хочется как-то сказать компилятору, что LDA внутри класса не меняется, и компилер вправе оптимизировать конструкции X(i,j) с постоянным i внутри цикла, но пока не придумал как. GNU сам в большей части такое делает, но хочется компилеру "навязать" свое мнение, чтоб наверняка.

 

То есть, ИМХО, более-менее решение найдено, спасибо всем большое за интересные обсуждения!

 

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

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


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

4 minutes ago, iiv said:

Из пожеланий хочется как-то сказать компилятору, что LDA внутри класса не меняется,

template <typename T> class MDA2
{ public:
  T *A;
  const int LDA;
  MDA2(T *aA, int aLDA) : LDA(aLDA) { A=aA; };
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};

 

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


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

14 minutes ago, xvr said:

template <typename T> class MDA2
{ public:
  T *A;
  const int LDA;
  MDA2(T *aA, int aLDA) : LDA(aLDA) { A=aA; };
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};

 

Супер, спасибо большое!!!

 

А как еще сказать, что указатель (*A) - тоже константа, но сделать так, что *A конечно можно было бы менять? По аналогии попробовал, но не получилось.

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


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

35 минут назад, iiv сказал:

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

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

В любом случае, рад, что идея помогла.

18 минут назад, iiv сказал:

А как еще сказать, что указатель (*A) - тоже константа, но сделать так, что *A конечно можно было бы менять? По аналогии попробовал, но не получилось.

   T *const A;

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


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

46 минут назад, iiv сказал:

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

Зачем? В бусте же есть, вполне нормальная реализация.

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


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

Кстати, вспомнил. В gsl есть multi_span. По идее, это должно идеально подойти.

(не смотрите, что это микрософт, это должно работать в любом современном компиляторе)

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


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

2 часа назад, iiv сказал:

Из пожеланий хочется как-то сказать компилятору, что LDA внутри класса не меняется, и компилер вправе оптимизировать конструкции X(i,j) с постоянным i внутри цикла, но пока не придумал как. GNU сам в большей части такое делает, но хочется компилеру "навязать" свое мнение, чтоб наверняка.

template <typename T, int aLDA> class MDA2 { 
  enum : int { LDA = aLDA };
  public:
  T *A;
  MDA2(T *aA) : { A=aA; };
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};

Если вы конечно не используете экземпляр класса повторно с переинициализацией LDA

 

Чой то я, там enum вообще не нужен - используйте параметр шаблона напрямую

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


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

23 minutes ago, Nixon said:

Если вы конечно не используете экземпляр класса повторно с переинициализацией LDA 

не, похоже так не получится, компилер ругается, что

error: ‘N’ is not a constant expression
 { MDA2<double, N> X(_X);

 

на момент инициализации - это как-то вычисленная переменная или входной аргумент в функции, а вот потом - действительно константа.

 

2 hours ago, AHTOXA said:

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

В любом случае, рад, что идея помогла.

Мне Ваша идея реально очень нравится, и видится более разумной, чем модификация

х[i][j][k] -> (i,j,k)

но компилятор-то тупой.

У меня часто возникает конструкция

for(j=0; j<M; j++)
  ... X[i][j][k] ... 

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

XX = X + k + N*M*i;

и обращение выполняется как XX[j*N]

но по-видимому GNU тупит и не может эту конструкцию увидеть. На моем классе он таки делает то, что надобно. На других компилерах не пробовал, только проверил, что все стандартно компилится.

 

2 hours ago, AHTOXA said:

T *const A;

Спасибо большое!!!

Я С++ знаю только на уровне еще очень старого стандарта 2.0 (89 год кажется) и с тех пор только на фортране и С программировал и в новых стандартах пока подтормаживаю, спасибо большое, что на путь истинный наставили!

 

2 hours ago, Kabdim said:

Зачем? В бусте же есть, вполне нормальная реализация.

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

 

51 minutes ago, AHTOXA said:

Кстати, вспомнил. В gsl есть multi_span. По идее, это должно идеально подойти.

Спасибо! Читаю, разбираюсь. Спасибо большое!!!

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


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

Позвольте, пожалуйста, еще вопрос в тему, чтоб и рыбку съесть и ...

Хочу компиллеру объяснить, что все внутренности класса константы, но иногда таки нет :)

 

#include <math.h>


template <typename T> class MDA2
{ public:
  const int LDA;
  T *const A;
  MDA2(T *aA, int aLDA) : LDA(aLDA), A(aA) {};
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};


void Func3(int N, int M, double *_X)
{ MDA2<double> X(_X, N);

  auto Test = [&] () -> void
  { int i, j;
    for(i=0; i<N; i++)
      for(j=0; j<M; j++)
        X(i,j)=(double)(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;
  }

// то есть до этого места все внутренности этого класса постоянны, и компилер может все отоптимизировать,
  &X=MDA2<double>(_X+5, N*2); // а вот тут я хочу и адрес новый присвоить, и размерность поменять, но из-за
  // отсутствия опыта написания таких извращений не могу побкедить синтаксис, посоветуйте, пожалуйста, как это сделать!

  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;
}

Спасибо!

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


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

1 час назад, iiv сказал:

void Func3(int N, int M, double *_X)
{
  MDA2<double> X(_X, N);

Спасибо!

здесь указывать <double> необязательно, компилятор сам выведет тип:
  MDA2 X(_X, N):

1 час назад, iiv сказал:

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

  &X=MDA2<double>(_X+5, N*2); // а вот тут я хочу и адрес новый присвоить, и размерность поменять, но из-за
  // отсутствия опыта написания таких извращений не могу побкедить синтаксис, посоветуйте, пожалуйста, как это сделать!

Никаких извращений не надо. Либо вы просто присваиваете переменной X новое значение:

X = MDA2(...);

либо заводите другую переменную:

MDA X2(..);

и работаете с ней.

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


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

Огромное спасибо, AHTOXA за постые и понятные советы!!!
Действительно <double> с 17 стандарта вроде бы и не нужен, спасибо!!!

 

А вот с присвоением - так и не понял можно ли или нет...

 

#include <math.h>


template <typename T> struct MDA2
{ const int LDA; // да, я понимаю, что можно убрать отсюда const и тогда то, что внизу будет работать,
  T *const A; // но хочется как-то предупредить компилер, что между такой инициализацией A и LDA не меняются
  MDA2(T *aA, int aLDA) : LDA(aLDA), A(aA) {};
  T &operator()(int i1, int i2) { return A[i2+LDA*i1]; };
  T *operator()(int i1) { return A+LDA*i1; };
};


void Func3(int N, int M, double *_X)
{ MDA2 X(_X, N);

  auto Test = [&] () -> void
  { int i, j;
    for(i=0; i<N; i++)
      for(j=0; j<M; j++)
        X(i,j)=(double)(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;
  }

  X=MDA2(_X+5, N*2);

  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;
}

Спасибо!

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


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

А, ну да, точно. Там же константные члены, оператор присваивания не сработает.

Ну тогда создайте новую переменную:

  MDA2 X2(_X+5, N*2);

и не забивайте себе голову:)

Это как раз и позволит сказать компилятору, что в цикле можно оптимизировать по полной, после цикла передвинуть эти константы в другое положение, и затем снова оптимизировать по полной. Причём сказать это полностью прозрачно, понятно для компилятора, понятно для человека, читающего этот код, без всяких хаков и ухищрений:)

 

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

 

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


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

Точно, спасибо огромное!!! Я еще не до конца подсекаю концепцию С++ и часто делаю по С-шному, а Вы так классно и понятно объяснили!!!

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


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

15 минут назад, iiv сказал:

Я еще не до конца подсекаю концепцию С++...

image.png.591b44fc2bcd01afda42a0d2430a4e66.png

 

Я вот вообще не понял, что происходит в этой теме:biggrin:

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


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

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

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

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

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

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

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

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

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

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