[英]CUDA Array of Structs With Arrays (AoSoA)
注4
因此,代碼終於確定了! 原來的最后一個問題是我正在將分配給每個數組的空間大小添加到ptr,但是c已經考慮到了變量的大小,所以我實質上是在添加4倍的字節空間因此,只會顯示5元素數組中的前兩個元素。 AoSoA
現在可以完全正常工作。 請注意您的記憶。 如果您嘗試類似的管理,我會遇到很多看似愚蠢的錯誤,因為我的初始代碼草率。
謹防:
+偏移量不正確
+不必要的malloc
+超出范圍的參考
這是工作示例代碼,結果如下!
#include <stdio.h>
#define REGIONS 20
#define YEARS 5
__inline __host__ void gpuAssert(cudaError_t code, char *file, int line,
bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
struct AnimalPopulationForYear_s
{
bool isYearEven;
int * rabbits;
int * hyenas;
};
AnimalPopulationForYear_s * dev_pop;
__global__ void RunSim(AnimalPopulationForYear_s dev_pop[],
int year)
{
int idx = blockIdx.x*blockDim.x+threadIdx.x;
int rabbits, hyenas;
int arrEl = year-1;
rabbits = (idx+1) * year * year;
hyenas = rabbits / 10;
if ( rabbits > 100000 ) rabbits = 100000;
if ( hyenas < 2 ) hyenas = 2;
if ( idx < REGIONS ) dev_pop[arrEl].rabbits[idx] = rabbits;
if ( idx < REGIONS ) dev_pop[arrEl].hyenas[idx] = hyenas;
if (threadIdx.x == 0 && blockIdx.x == 0)
dev_pop[arrEl].isYearEven = (year & 0x01 == 0x0);
}
int main()
{
//Various reused sizes...
const size_t fullArrSz = size_t(YEARS) * size_t(REGIONS) * sizeof(int);
const size_t structArrSz = size_t(YEARS) * sizeof(AnimalPopulationForYear_s);
//Vars to hold struct and merged subarray memory inside it.
AnimalPopulationForYear_s * h_pop;
int * dev_hyenas, * dev_rabbits, * h_hyenas, * h_rabbits, arrEl;
//Alloc. memory.
h_pop = (AnimalPopulationForYear_s *) malloc(structArrSz);
h_rabbits = (int *) malloc(fullArrSz);
h_hyenas = (int *) malloc(fullArrSz);
gpuErrchk(cudaMalloc((void **) &dev_pop,structArrSz));
gpuErrchk(cudaMalloc((void **) &dev_rabbits,fullArrSz));
gpuErrchk(cudaMalloc((void **) &dev_hyenas,fullArrSz));
//Offset ptrs.
for (int i = 0; i < YEARS; i++)
{
h_pop[i].rabbits = dev_rabbits+i*REGIONS;
h_pop[i].hyenas = dev_hyenas+i*REGIONS;
}
//Copy host struct with dev. pointers to device.
gpuErrchk
(cudaMemcpy(dev_pop,h_pop, structArrSz, cudaMemcpyHostToDevice));
//Call kernel
for(int i=1; i < YEARS+1; i++) RunSim<<<REGIONS/128+1,128>>>(dev_pop,i);
//Make sure nothing went wrong.
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
gpuErrchk(cudaMemcpy(h_pop,dev_pop,structArrSz, cudaMemcpyDeviceToHost));
gpuErrchk
(cudaMemcpy(h_rabbits, dev_rabbits,fullArrSz, cudaMemcpyDeviceToHost));
gpuErrchk(cudaMemcpy(h_hyenas,dev_hyenas,fullArrSz, cudaMemcpyDeviceToHost));
for(int i=0; i < YEARS; i++)
{
h_pop[i].rabbits = h_rabbits + i*REGIONS;
h_pop[i].hyenas = h_hyenas + i*REGIONS;
}
for(int i=1; i < YEARS+1; i++)
{
arrEl = i-1;
printf("\nYear %i\n=============\n\n", i);
printf("Rabbits\n-------------\n");
for (int j=0; j < REGIONS; j++)
printf("Region: %i Pop: %i\n", j, h_pop[arrEl].rabbits[j]);;
printf("Hyenas\n-------------\n");
for (int j=0; j < REGIONS; j++)
printf("Region: %i Pop: %i\n", j, h_pop[arrEl].hyenas[j]);
}
//Free on device and host
cudaFree(dev_pop);
cudaFree(dev_rabbits);
cudaFree(dev_hyenas);
free(h_pop);
free(h_rabbits);
free(h_hyenas);
return 0;
}
[最后]正確的結果:
1年級
=============
兔子
-------------
地區:0流行音樂:1
地區:1流行音樂:2
地區:2流行音樂:3
地區:3流行音樂:4
地區:4流行音樂:5
地區:5流行音樂:6
地區:6流行音樂:7
地區:7流行音樂:8
地區:8流行音樂:9
地區:9流行音樂:10
地區:10流行音樂:11
地區:11流行音樂:12
地區:12流行音樂:13
地區:13流行音樂:14
地區:14流行音樂:15
地區:15流行音樂:16
地區:16流行音樂:17
地區:17流行音樂:18
地區:18流行音樂:19
地區:19流行音樂:20
鬣狗
-------------
地區:0流行音樂:2
地區:1流行音樂:2
地區:2流行音樂:2
地區:3流行音樂:2
地區:4流行音樂:2
地區:5流行音樂:2
地區:6流行音樂:2
地區:7流行音樂:2
地區:8流行音樂:2
地區:9流行音樂:2
地區:10流行音樂:2
地區:11流行音樂:2
地區:12流行音樂:2
地區:13流行音樂:2
地區:14流行音樂:2
地區:15流行音樂:2
地區:16流行音樂:2
地區:17流行音樂:2
地區:18流行音樂:2
地區:19流行音樂:2
2年級
=============
兔子
-------------
地區:0流行音樂:4
地區:1流行音樂:8
地區:2流行音樂:12
地區:3流行音樂:16
地區:4流行音樂:20
地區:5流行音樂:24
地區:6流行音樂:28
地區:7流行音樂:32
地區:8流行音樂:36
地區:9流行音樂:40
地區:10流行音樂:44
地區:11流行音樂:48
地區:12流行:52
地區:13流行音樂:56
地區:14流行音樂:60
地區:15流行音樂:64
地區:16流行音樂:68
地區:17流行音樂:72
地區:18流行音樂:76
地區:19流行音樂:80
鬣狗
-------------
地區:0流行音樂:2
地區:1流行音樂:2
地區:2流行音樂:2
地區:3流行音樂:2
地區:4流行音樂:2
地區:5流行音樂:2
地區:6流行音樂:2
地區:7流行音樂:3
地區:8流行音樂:3
地區:9流行音樂:4
地區:10流行音樂:4
地區:11流行音樂:4
地區:12流行:5
地區:13流行音樂:5
地區:14流行音樂:6
地區:15流行音樂:6
地區:16流行音樂:6
地區:17流行音樂:7
地區:18流行音樂:7
地區:19 ...
注3:
在執行標准操作之后,清理了我的代碼中的多個數組索引不一致等情況。
對於AoSoA中的前兩個位置,結果看起來似乎是正確的SoA(請參閱新輸出)。 出於某種原因,盡管GPU沒有提供錯誤代碼,但第三點( year 3
)以后的結果現在給出了錯誤的結果。 我將偷看指針( h_pop[year-1].rabbits
, h_pop[year-1].hyenas
),看看是否能顯示任何內容。
對於其他嘗試AoSoA的人,我的唯一建議是-謹慎對待索引和內存分配。 當然,通常這是個好建議,但是隨着所有內存在復雜的多層數據容器(如AoSoA)中四處飛行,如果馬虎的話,出錯的可能性就會成倍增加。 感謝您的耐心, talonmies 。
筆記2:
所以,以下的建議talonmies ,我定我的循環#ing,包裹着我的CUDA電話W上。 錯誤檢查並通過重用dev_rabbits
/ dev_hyenas
壓縮了我的cudaMemcpy
調用。 當我想到[djmj] [4]關於大小寫的投訴時,也將大小寫改為小寫,我意識到NVIDIA確實將常量中的第一個字母小寫,所以[djmj] [4]是是的,從某種意義上說,無論我個人喜好/經驗如何,我都應該為代碼設置樣式以便保持一致。
通常還清理代碼,因為我編寫的代碼睡眠時間不長,因此有點草率。
現在,我遇到了一個新問題……我的程序在第一個cudaMemcpy
掛起,並且不返回(因此talonmies的便捷包裝器無法捕獲任何內容)。 我不太確定為什么會這樣...我已經編譯了幾個程序,包括設備上運行時間更長或更長時間的程序,它們都可以正常工作。
在這一點上,我感到困惑。 如果仍然無法正常工作,可能會在早上發布一些內容。
注1
第一個答案似乎真的不對勁。 這只是一個玩具代碼,並不代表真實的程序。 其唯一目的是嘗試設置內存,向其中寫入一些垃圾,然后將其讀回,以驗證AoSoA是否正常工作。
因此,對我發表有關共享內存等的評論將不會有成效。 那不是這個線程的重點。 當然,如果這是一個真實的代碼,我將消除內核中的分支,使用共享內存,對齊我的數據,使用翹曲級別求和等。我已經完成了過去的代碼中的所有工作並使它工作。
該代碼是玩具,概念證明代碼,僅此而已,旨在使AoSoA正常工作。 那只是它的唯一目的,不是真正的代碼。 這是概念的證明。
至於var名稱的大小寫,我在兩個不同的地方工作過,它們在其編碼標准中使用了全大小寫的var名稱(它們使用的標記,我在structs / _s
上執行_s
),因此有點卡住了。 抱歉,您不喜歡它。 至於縮進,我稍后會嘗試解決。Windows和Linux的表現不佳。
還有一點需要注意的是,如果您對設備指針偏移感到困惑,請在此處查看Anycom的答案:
傳遞給CUDA的結構體中的指針
我寫了以下代碼來測試結構數組,其中的數組在CUDA中。
編輯:固定代碼-掛在meh
之后和hi
之前,大概掛在cudaMemcpy
...不確定原因!
...知道這里發生了什么以及如何解決?
注意:我擔心cudaFree
可能會cudaFree
事情,但是刪除它們卻無濟於事。 [4]:
這段代碼有很多錯誤,但是您要查詢的“亂碼”結果的根本原因是,您正在查看未初始化的內存。 dev_Pop[0].Rabbits
永遠不會設置為設備內存中的任何內容,因此您不必為它的內容“亂碼”感到驚訝。 問題的根本原因是:
for(int i=1; i < YEARS+1; i++)
RunSim<<<REGIONS/128+1,128>>>(dev_Pop,i);
在這里,您從year=1
,這意味着year=0
從未設置為任何值, year=YEARS
是保證的設備內存緩沖區溢出。
在回寫代碼的后面,您可以在每次迭代中執行此操作:
cudaFree(h_Pop[i].Rabbits);
cudaFree(h_Pop[i].Hyenas);
但是您從來沒有首先分配過它們,因此回寫操作也可能會失敗。 如果不編譯和運行代碼,很難說如何失敗,但是我想 CUDA運行時將在第一次調用時完全釋放dev_Rabbits
和dev_Hyenas
。 這會使循環中的后續cudaMemcpy
調用失敗。 無論使用哪種精確的機制,如果您的復制回送循環成功將所有數據返回給主機,我都會感到非常驚訝。 更加明智的實現將是與您最初用於構建設備內存映像的代碼類似的工作,例如:
const size_t dsize = size_t(YEARS) * size_t(REGIONS) * sizeof(int);
int * Rabbits = (int *) malloc(dsize);
int * Hyenas = (int *) malloc(dsize);
cudaMemcpy(Rabbits, dev_Rabbits, dsize, cudaMemcpyDeviceToHost);
cudaMemcpy(Hyenas, dev_Hyenas, dsize, cudaMemcpyDeviceToHost);
for(int i=0; i < YEARS; i++)
{
h_Pop[i].Rabbits = Rabbits + i*REGIONS;
h_Pop[i].Hyenas = Hyenas + i*REGIONS;
}
這樣做可以消除PCI-e總線上的大量冗余設備->主機事務,並消除循環中所有不必要的主機端malloc
調用。
因此,我猜想代碼中會發生多處運行時故障,但是由於您忽略了任何錯誤檢查,因此一切都在靜默地失敗,並且您根本沒有注意到。 要解決此問題,請在您的代碼中添加以下內容:
inline void gpuAssert(cudaError_t code, char * file, int line, bool Abort=true)
{
if (code != cudaSuccess) {
fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code),file,line);
if (Abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
然后使用gpuErrchk
測試每個 API調用的返回狀態,例如:
gpuErrchk(cudaMalloc((void **) &dev_Pop,YEARS*sizeof(AnimalPopulationForYear_s)));
對於您的內核啟動,我建議這樣做:
RunSim<<<REGIONS/128+1,128>>>(dev_Pop,i);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
這將同時捕獲會導致啟動失敗的非法參數和資源耗盡,以及將導致內核中止的任何執行錯誤。 有了這個錯誤檢查,我懷疑您會在代碼實際運行完成之前發現很多漏洞需要解決。
編輯:
看來您已決定發明新的和不尋常的方法來使修改后的代碼不起作用-包括破壞您在原始代碼中正確的東西,並且似乎是您所要解決的問題-構造設備存儲器陣列結構。
這是第二個代碼的稍微簡化的工作版本。 我所建議的就是研究它,直到您了解在當前版本失敗的地方它為何起作用為止。
#include <cstdio>
#include <cstdlib>
#define REGIONS 20
#define YEARS 5
#define POPMIN 2
#define POPMAX 100000
inline void gpuAssert(cudaError_t code, char *file, int line,
bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
struct Population_s
{
int * rabbits;
int * hyenas;
};
__global__ void RunSim(Population_s * dev_pop, int year)
{
int idx = blockIdx.x*blockDim.x+threadIdx.x;
if (idx < REGIONS) {
int rabbits, hyenas;
rabbits = min(POPMAX, idx * year * year);
hyenas = max(POPMIN, rabbits / 10);
dev_pop[year-1].rabbits[idx] = rabbits;
dev_pop[year-1].hyenas[idx] = hyenas;
}
}
int main()
{
const size_t subArrSz = size_t(REGIONS) * sizeof(int);
const size_t fullArrSz = size_t(YEARS) * subArrSz;
const size_t structArrSz = size_t(YEARS) * sizeof(Population_s);
Population_s * h_pop = (Population_s *) malloc(structArrSz);
int * h_rabbits = (int *) malloc(fullArrSz);
int * h_hyenas = (int *) malloc(fullArrSz);
Population_s * dev_pop;
int * dev_hyenas, * dev_rabbits;
gpuErrchk(cudaMalloc((void **) &dev_pop,structArrSz));
gpuErrchk(cudaMalloc((void **) &dev_hyenas,fullArrSz));
gpuErrchk(cudaMalloc((void **) &dev_rabbits,fullArrSz));
gpuErrchk(cudaMemset(dev_rabbits, 1, fullArrSz));
gpuErrchk(cudaMemset(dev_hyenas, 1, fullArrSz));
for (int i = 0; i < YEARS; i++)
{
h_pop[i].rabbits = dev_rabbits + i*REGIONS;
h_pop[i].hyenas = dev_hyenas + i*REGIONS;
}
gpuErrchk
(cudaMemcpy(dev_pop,h_pop, structArrSz, cudaMemcpyHostToDevice));
for(int i = 1; i < (YEARS+1); i++) {
RunSim<<<REGIONS/128+1,128>>>(dev_pop,i);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
}
gpuErrchk(cudaMemcpy(h_rabbits, dev_rabbits, fullArrSz, cudaMemcpyDeviceToHost));
gpuErrchk(cudaMemcpy(h_hyenas, dev_hyenas, fullArrSz, cudaMemcpyDeviceToHost));
for(int i=0; i < YEARS; i++)
{
h_pop[i].rabbits = h_rabbits + i*REGIONS;
h_pop[i].hyenas = h_hyenas + i*REGIONS;
}
for(int i=0; i < YEARS; i++)
{
printf("\n=============\n");
printf("Year %i\n=============\n\n", i+1);
printf("Rabbits\n-------------\n", i);
for (int j=0; j < REGIONS; j++)
printf("Region: %i Pop: %i\n", j, h_pop[i].rabbits[j]);;
printf("\nHyenas\n-------------\n", i);
for (int j=0; j < REGIONS; j++)
printf("Region: %i Pop: %i\n", j, h_pop[i].hyenas[j]);
}
cudaFree(dev_pop);
cudaFree(dev_rabbits);
cudaFree(dev_hyenas);
free(h_pop);
free(h_rabbits);
free(h_hyenas);
return 0;
}
最后提示- 不要在您自己的代碼中使用SDK cutil
庫中的任何東西,這不是它想要的。 它不是CUDA的正式組成部分,沒有文檔,沒有被考慮用於生產,並且不能保證在任何給定的CUDA SDK版本中都能正常工作,相同甚至存在。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.