функтор с внутренним состоянием в тяге

Я пытаюсь пройти через два вектора, вычислить разницу между координатами, вычислить еще немного силы, используя массу и т. д., и актуализировать значение ускорения в каждом цикле, который выполняет тяга:: for_each. Однако я не могу отслеживать значение ускорения.

Используя Thrust и функторы, мне удалось написать это:

Это функтор, который вызывает у меня проблемы:

struct ParticleGenerator::acc_func{
    //stores the initial coordinates if i-th particle to use it for computation and the init. acc. that is 0.0f
    acc_func(float Ax, float Bx, float Cx, int X, int Y, int Z) : ax(Ax), bx(Bx), cx(Cx),  _x(X), _y(Y), _z(Z) {}

    template <typename Tuple>
    __device__ __host__
    void operator()(Tuple t){
            //thrust::get<0>(t) +=(int) 32;
            // save some values
            acc[0] = thrust::get<0>(t);  //(#)
            acc[1] = thrust::get<1>(t);
            acc2[0] = acc[0];
            acc2[1] = acc[1];
            //retrieve them, OK
            printf("%d_%d\n", acc[0], acc[1]); //works well
    }


    int getRes(){
            //return saved values, not OK
            printf("%d_%d_%d_%d\n", acc[0], acc[1], acc2[0], acc2[1]); //prints wrong values

            return 0;
    }

    //this returns the correct value, though
    int getRes2(){ return _x;}

    int acc2[2];
private:
        float ax, bx, cx;
        int _x, _y, _z;
        int temp;
        int acc[2];
};

как видите, я пробовал как публичный, так и приватный, а также пытался использовать простое int (temp) для хранения любого значения, кроме функции getRes() при использовании в качестве ниже никогда не возвращает правильное значение.

Я заметил, что часть _x(X) (прокрутите вправо, чтобы увидеть полный конструктор acc_func()) правильно сохраняет значение, и я могу получить его с помощью функции getRes( ).

ВОПРОС. Есть ли способ воспроизвести это поведение? Чтобы использовать команду в строке, отмеченной //(#), и успешно сохранить, обновить и позже вернуть значение?

и это моя петля тяги:

for(unsigned int i = 0; i < vecPosX.size(); ++i){   
    acc_func AF(0.0f, 0.0f, 0.0f, vecPosX[i], vecPosY[i], vecPosZ[i]);
    thrust::for_each(
                      thrust::make_zip_iterator(thrust::make_tuple(vecPosX.begin(), vecPosY.begin())),
                      thrust::make_zip_iterator(thrust::make_tuple(vecPosX.end(), vecPosY.end())),
                    AF
                    );
    AF.getRes();
    //use the AF.getRes() to save the values of the acceleration and
    //update the vecPosX[i], vecPosY[i] etc accordingly
}

где vecPosX, vecPosY — векторы, содержащие положения X и Y частицы.

вся идея состоит в том, чтобы создать кортеж (posX, posY) и в каждом цикле thrust::for_each пересчитывать и актуализировать ускорение, а когда вычисление завершено, просто возвращать результаты ускорения x и y, поэтому что я могу обновить скорость и положение i-й частицы

вот два разных результата:

0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 0
thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);
-588854768_32764 267_264_267_264 254_272_254_272 256_237_256_237 264_264_264_264 259_228_259_228 259_247_259_247 264_245_264_245 265_237_265_237 272_260_272_260

Я надеюсь, что вы можете мне помочь, спасибо :)

редактировать: обратите внимание, что я успешно написал все это приложение для моделирования, используя SDL, Visual Studio и два цикла for, один внутри другого, прямо сейчас я пытаюсь сделать его быстрее, чтобы иметь возможность использовать больше частиц

редактировать 2: чтобы воспроизвести проблему, можно использовать

thrust::device_vector<int> vecPosX(10, 10;
thrust::device_vector<int> vecPosY(10, 10);
thrust::device_vector<int> vecPosZ(10, 10);

для создания векторов и некоторых значений

изменить 3:

Прошу прощения за плохое объяснение, пожалуйста, потерпите меня. Вот полный, простой, компилируемый и работоспособный пример ожидаемых и неожиданных результатов:

пример.cu:

#include <thrust/device_vector.h>
#include <thrust/for_each.h>
#include <thrust/iterator/zip_iterator.h>
#include <iostream>

thrust::device_vector<int> vecPosX(5, 10);
thrust::device_vector<int> vecPosY(5, 10);

struct acc_func {
    acc_func(int X, int Y) : _x(X), _y(Y) {}

    template <typename Tuple>
    __device__ __host__
    void operator()(Tuple t){
            acc2[0] = _x;
            acc2[1] = _y;
            acc[0] += 1;
            acc[1] += 1;
    }


    void expected(){
        printf("expected:\t%d:%d:and:%d:%d\n", 5, 5, _x, _y);
        return;
    }

    void not_expected(){
        printf("unexpected:\t%d:%d:nor:%d:%d\n\n", acc[0], acc[1], acc2[0], acc2[1]);
        return;
    }

public:
        int acc2[2];
private:
        int _x, _y;
        int acc[2] = {0, 0};
};


int main(){

    for(unsigned int i = 0; i < vecPosX.size(); ++i){
        acc_func AF(vecPosX[i], vecPosY[i]);
        thrust::for_each(
            thrust::make_zip_iterator(thrust::make_tuple(vecPosX.begin(),vecPosY.begin())),                      

            thrust::make_zip_iterator(thrust::make_tuple(vecPosX.end(), vecPosY.end())),
            AF
          );
        AF.expected();
        AF.not_expected();
    }

    return 0;


}

Результаты:

$ nvcc -std=c++11 example.cu -o example
$ ./example
expected:       5:5:and:10:10
unexpected:     0:0:nor:19:0

expected:       5:5:and:10:10
unexpected:     0:0:nor:19:0

expected:       5:5:and:10:10
unexpected:     0:0:nor:19:0

expected:       5:5:and:10:10
unexpected:     0:0:nor:19:0

expected:       5:5:and:10:10
unexpected:     0:0:nor:19:0

изменить 4

Чего я пытаюсь добиться, так это переписать следующий код:

float ax, ay, az, dx, dy, dz;
float invr, invr3, f;
for(unsigned int i = 0; i < particles.size(); i++){
    ax = 0.0;
    ay = 0.0;
    az = 0.0;


    for(unsigned int j = 0; j < particles.size(); j++){
        dx = (float) (particles[j]->mPosX - particles[i]->mPosX);
        dy = (float) (particles[j]->mPosY - particles[i]->mPosY);
        dz = (float) (particles[j]->mPosZ - particles[i]->mPosZ);
        invr = (float) 1.0/sqrt(dx*dx + dy*dy + dz*dz + 100);
        invr3 = invr*invr*invr;
        f = particles[j]->mass * invr3;
        ax += f*dx;
        ay += f*dy;
        az += f*dz;
    }
    particles[i]->mPosX = particles[i]->mPosX + (int) dt*particles[i]->xVel + (int) 0.5*dt*dt*ax;
    particles[i]->mPosY = particles[i]->mPosY + (int) dt*particles[i]->yVel + (int) 0.5*dt*dt*ay;
    particles[i]->mPosZ = particles[i]->mPosZ + (int) dt*particles[i]->zVel + (int) 0.5*dt*dt*az;
    particles[i]->xVel += dt*ax;
    particles[i]->yVel += dt*ay;
    particles[i]->zVel += dt*az;
}

Мое намерение состояло в том, чтобы оставить внешний цикл таким, какой он есть, вычислить ax, ay, az, используя тягу (поскольку он проходит через кучу элементов в векторе) и обновить векторы с результатом тяги for_each.

Как я могу безопасно посчитать ax, ay и az и вернуть их?

частицы, которые вы видите,

std::vector<Particle *> particles;

а Particle — это класс, в котором есть переменные-члены.

int mPosX, mPoY, mPosZ;
float xVel, yVel, zVel;
int mass;

и дт:

const float dt = 0.1f;

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


person kn0t3k    schedule 16.08.2015    source источник
comment
Извините, но я прочитал это уже три раза, и я не понимаю, что вы пытаетесь здесь спросить. В вашем вопросе говорится, что я заметил, что часть _x (X) правильно сохраняет значение, но я не вижу ничего с таким именем нигде в опубликованном вами коде. Не могли бы вы немного упростить вопрос и объяснить, что именно вы пытаетесь здесь сделать?   -  person talonmies    schedule 16.08.2015
comment
вам нужно прокрутить вправо: acc_func(float Ax, float Bx, float Cx, int X, int Y, int Z): ax(Ax), bx(Bx), cx(Cx), _x(X), _y(Y ), _z(Z) {}. _x(X) сохраняет значение в _x, а затем из функции getRes я могу вернуть _x, и значение имеет смысл   -  person kn0t3k    schedule 16.08.2015
comment
пожалуйста, отредактируйте свой вопрос с помощью минимального, полного и проверяемого примера, включая образцы входных данных, текущий вывод и желаемый результат. как сейчас написано, я понятия не имею, что вы пытаетесь рассчитать и где это на самом деле терпит неудачу.   -  person m.s.    schedule 16.08.2015
comment
хорошо, я отредактировал сообщение, извините за шум связи   -  person kn0t3k    schedule 16.08.2015
comment
Хорошо, теперь все намного яснее. И здесь много неправильно.   -  person talonmies    schedule 16.08.2015


Ответы (1)


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

  1. В своем цикле вы создаете экземпляр acc_func и передаете его по значению вызову for_each. Графический процессор работает с копией AF. Поэтому, когда вы вызываете not_expected, вы распечатываете значения оригинала хоста, а не копии, с которой фактически работал GPU.

  2. Внутри функтора вы делаете это:

       acc[0] += 1;
       acc[1] += 1;
    

    Это гонки памяти. Каждый поток, запущенный тягой for_each (а вы не можете знать, сколько их), будет одновременно пытаться увеличить эти значения, что приведет к опасностям памяти чтения после записи. В CUDA есть функции атомарной памяти, но это был бы ужасно неэффективный способ добиться того, что пытается сделать ваш код.

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

Наконец, просто комментарий, но никогда не объявляйте векторы тяги в глобальном масштабе. См. здесь, почему.

person Community    schedule 16.08.2015
comment
спасибо за ваш ответ, как мне правильно передать AF, чтобы я читал из структуры, с которой работал GPU? Также я добавил дополнительную информацию о своем приложении, не могли бы вы взглянуть и указать мне правильное направление? - person kn0t3k; 16.08.2015
comment
@ kn0t3k: ответ одним предложением: вы не можете. Функторы не предназначены для использования в качестве контейнеров данных, и невозможно восстановить их состояние после параллельной операции с данными. Вам придется подумать о другом способе сделать это. - person talonmies; 16.08.2015
comment
У меня только что появилась идея, я мог бы создать еще три вектора, давайте назовем их vecAccX, vecAccY, vecAccZ, передаем их в make_tuple и сохраняем каждый результат цикла в один элемент этого вектора vecAcc с помощью итератора. Когда вычисления завершены, я могу просто добавить все элементы и получить результат. Однако я не уверен, что это решение займет много времени GPU/памяти ... но я думаю, что это хорошая отправная точка. - person kn0t3k; 16.08.2015