CUDA Thrust - Как мога да напиша функция, използвайки множество вектори на устройства с различни размери?

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

Имам четири вектора на устройства, представляващи две двойки ключ-стойност. Първата двойка вектори съдържа ключовете и броя на появяванията на този ключ. Втората двойка съдържа ключове, свързани с контейнерите за изчисляване на ентропията. В тази втора векторна двойка ключовете се появяват няколко пъти, като всеки екземпляр представлява различен кош.

Изглежда нещо подобно:

Векторна двойка устройство 1

KeyVal 6 8 9

Брои 1 3 2

Векторна двойка устройство 2

KeyVal 6 8 8 9 9

BinVal 1 1 2 1 1

Резултат вектор (съдържа изчислените резултати за ентропия)

KeyVal 8

Ентропия 0,602

Това, което планирам да направя, е да използвам първата векторна двойка, за да проверя дали даден ключ се появява достатъчно пъти, за да изчисля ентропията. Ако броят е достатъчно голям, втората векторна двойка ще се използва за изчисляване на ентропията със стойностите на bin за този ключ. Ще трябва да използвам всички стойности на bin за този конкретен ключ. Например, ако исках да изчисля ентропията за ключове, които са се появили поне 3 пъти, щях да открия в първата векторна двойка, че KeyVal 8 е готов. След това бих търсил във втората двойка всички екземпляри на KeyVal 8 и бих изчислил ентропията, използвайки съответните им BinVals. Изчисляването на ентропията е просто, то просто включва събиране на BinVal*Log(BinVal) за всяка съответна стойност. В моя пример ще бъде ентропия = 1*log(1) + 2*log(2).

Въпреки това нямам представа как да накарам тази част да работи. Опитах се да използвам thrust::for_each, за да намеря всички ключове, които се появяват достатъчно пъти, за да бъдат тествани, но не мисля, че е възможно да се търсят ключовете във втората векторна двойка и да се извърши изчислението във функцията for_each.

Някой има ли предложения за други начини да се постигне това?

Благодаря ти за помощта.


person user2471905    schedule 02.07.2015    source източник
comment
можеш ли да завършиш описанието си? осигурете пълна последователност от вектори, включително желания резултатен вектор за вашия пример. По-конкретно, вие казвате Тогава бих търсил във втората двойка всички екземпляри на KeyVal 8 и бих изчислил ентропията, използвайки съответните им BinVals. Какво означава това? Каква точно е аритметиката, която искате да извършите върху BinVals, които съответстват на KeyVal 8 (т.е. BinVals 1 и 2 във вашия пример)?   -  person Robert Crovella    schedule 02.07.2015
comment
Съжалявам, трябваше да включа това. Изчислението е просто, просто се събира BinVal*Log(BinVal) за всяка съответна стойност. В моя пример ще бъде ентропия = 1*log(1) + 2*log(2).   -  person user2471905    schedule 02.07.2015
comment
Сортирани ли са векторите KeyVal? Ако не, случайно подобни ключове са групирани заедно? И бих ли очаквал ключовете да се появяват в същия ред, във всеки вектор? Ако конкретен KeyVal се появи в първия вектор KeyVal, има ли възможност да няма няма записи за него във втория вектор KeyVal?   -  person Robert Crovella    schedule 02.07.2015
comment
Векторите KeyVal са сортирани, като подобните ключове са групирани заедно.   -  person user2471905    schedule 02.07.2015
comment
Ако конкретен KeyVal се появи в първия вектор KeyVal, има ли възможност да няма записи за него във втория вектор KeyVal?   -  person Robert Crovella    schedule 02.07.2015
comment
Не, ако KeyVal се появи в първия, той гарантирано ще се появи и във втория вектор KeyVal.   -  person user2471905    schedule 02.07.2015
comment
Съжалявам за всички въпроси. Ако KeyVal се появи във втория вектор KeyVal, има ли възможност да няма записи за него в първия вектор KeyVal?   -  person Robert Crovella    schedule 02.07.2015
comment
Няма проблем. Ако KeyVal се появи в един вектор, тогава той трябва да се появи и в другия.   -  person user2471905    schedule 02.07.2015
comment
не трябва ли да е Counts 1 2 2 ? Или тези преброявания не отчитат колко често се появява ключ във втория KeyVal вектор?   -  person m.s.    schedule 02.07.2015
comment
Преброяването отчита общия брой BinVal за този KeyVal.   -  person user2471905    schedule 02.07.2015


Отговори (1)


Двете идеи, които разгледах, бяха:

Идея А:

  1. Изчислете всички ентропии
  2. Изберете тези, които отговарят на критериите

Идея Б:

  1. Изберете входящите данни, които отговарят на критериите
  2. Изчислете ентропиите.

Идея А изглежда върши ненужна работа - изчислява ентропии, които са или може да не са необходими. Въпреки това, докато работих през процеса за Идея Б, в крайна сметка добавих толкова много стъпки (като изчисляване на префиксни суми), за да завърша стъпка 1 от Идея Б, че не изглеждаше по-добре. Така че засега ще представя идея А. Може би м.с. или някой друг ще дойде и ще публикува нещо по-добро.

Стъпка 1 от идея A се обработва от thrust::reduce_by_key заедно с подходящ функтор за изчисляване на специфичната ентропийна функция

Стъпка 2 от идея A се обработва от thrust::copy_if

$ cat t827.cu
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/reduce.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/discard_iterator.h>
#include <math.h>

// THRESH determines the minimum Counts value required for a KeyVal Entropy calculation to occur
#define THRESH 2

using namespace thrust::placeholders;


struct my_entropy : public thrust::unary_function<float, float>
{
  __host__ __device__
  float operator()(float val){
    return val*log10f(val);}  // if you want napierian log, change this to logf
};

int main(){

  int KeyVal1[]={6, 8, 9};
  int Counts[] ={1, 3, 2};
  int KeyVal2[]={6, 8, 8, 9, 9};
  float BinVal[] ={1, 1, 2, 1, 1};

  int dsize1 = sizeof(KeyVal1)/sizeof(int);
  int dsize2 = sizeof(KeyVal2)/sizeof(int);

  thrust::device_vector<int> d_KeyVal1(KeyVal1, KeyVal1+dsize1);
  thrust::device_vector<int> d_Counts(Counts, Counts+dsize1);
  thrust::device_vector<int> d_KeyVal2(KeyVal2, KeyVal2+dsize2);
  thrust::device_vector<float> d_BinVal(BinVal, BinVal+dsize2);


  // method 1 - just compute all entropies, then select the desired ones
  thrust::device_vector<float> entropies(dsize2);
  thrust::reduce_by_key(d_KeyVal2.begin(), d_KeyVal2.end(), thrust::make_transform_iterator(d_BinVal.begin(), my_entropy()), thrust::make_discard_iterator(), entropies.begin());
  thrust::device_vector<int> res_keys(dsize1);
  thrust::device_vector<float>res_ent(dsize1);
  int res_size = thrust::copy_if(thrust::make_zip_iterator(thrust::make_tuple(d_KeyVal1.begin(), entropies.begin())), thrust::make_zip_iterator(thrust::make_tuple(d_KeyVal1.end(), entropies.end())), d_Counts.begin(), thrust::make_zip_iterator(thrust::make_tuple(res_keys.begin(), res_ent.begin())), _1 >= THRESH) - thrust::make_zip_iterator(thrust::make_tuple(res_keys.begin(), res_ent.begin()));
  std::cout << "Counts threshold: " << THRESH << std::endl <<  "selected keys: " << std::endl;
  thrust::copy_n(res_keys.begin(), res_size, std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "calculated entropies: " << std::endl;
  thrust::copy_n(res_ent.begin(), res_size, std::ostream_iterator<float>(std::cout, ","));
  std::cout << std::endl;

  return 0;
}
[bob@cluster1 misc]$ nvcc -o t827 t827.cu
$ ./t827
Counts threshold: 2
selected keys:
8,9,
calculated entropies:
0.60206,0,
$
person Robert Crovella    schedule 02.07.2015
comment
Благодаря ти! Тествах идея А с моя код и проверих, че дава правилната ентропия за моите данни. - person user2471905; 02.07.2015