我正在嘗試分配一個結構陣列,每個結構還包含動態陣列。他們稍后將通過以下方式進行溝通MPI_Sendrecv:
struct cell {
double a, b, c, *aa, *bb;
} *Send_l, *Send_r;
我想要Send_l并且Send_r擁有count多個元素,陣列aa并且bb應該包含sAS多個元素。這都是在 之后完成的MPI_Init。
void allocateForSendRecv(int count) {
int sAS = 5;
int iter = 0;
Send_l = (struct cell *)malloc(count * (sizeof(struct cell)));
for (iter = 0; iter < count; iter ) {
Send_l[iter].aa = (double *)malloc((sAS - 1) * sizeof(double));
Send_l[iter].bb = (double *)malloc((sAS - 1) * sizeof(double));
}
//sAS-1, as sizeof(struct cell) already contains a single (double) for aa and bb.
Send_r = (struct cell *)malloc(count * (sizeof(struct cell)));
for (iter = 0; iter < count; iter ) {
Send_r[iter].aa = (double *)malloc((sAS - 1) * sizeof(double));
Send_r[iter].bb = (double *)malloc((sAS - 1) * sizeof(double));
}
}
有了這個,我可以自由地分配、填充和解除分配,但是當我呼叫以下命令時,我的結果與我的參考不同(使用所有堆疊陣列)。
MPI_Sendrecv(&(Send_r[0]), count, ..., &(Send_l[0]), count, ...)
我還沒有找到確切的原因,但是關于類似問題的帖子讓我認為這是由于我的記憶體分配不連續。我試圖通過使用一個 malloc 呼叫來解決這個問題,只是在我填充我的陣列時出現分段aa錯誤bb:
Send_l = malloc(count * (sizeof(*Send_l)) count *(sizeof(*Send_l) 2 * (sAS - 1) * sizeof(double)));
Send_r = malloc(count * (sizeof(*Send_r)) count *(sizeof(*Send_r) 2 * (sAS - 1) * sizeof(double)));
我重用了一些代碼來分配二維陣列并將其應用于這個結構問題,但無法使其作業。我是否正確地假設,通過一個有效的單個malloc呼叫并因此連續的記憶體分配,我MPI_Sendrecv可以正常作業?或者,使用會MPI_Type_create_struct解決我的非連續記憶體問題嗎?
分段錯誤的最小示例(沒有 MPI)。使用allocateSendRecv,一切都很好。但是單個 alloc inallocateInOneSendRecv給我帶來了問題。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
struct cell {
double a, b, c, *aa, *bb;
} *Send_l, *Send_r;
void allocateSendRecv(int count, int sAS);
void fillSendRecv(int count, int sAS);
void freeSendRecv(int count);
void printSendRecv(int count, int sAS);
void allocateInOneSendRecv(int count, int sAS);
int main(int argc, char *argv[])
{
const int count = 2;
const int sAS = 9;
allocateSendRecv(count, sAS);
//allocateInOneSendRecv(count, sAS);
fillSendRecv(count, sAS);
printSendRecv(count, sAS);
freeSendRecv(count);
return 0;
}
void allocateSendRecv(int count, int sAS) {
int iter = 0;
printf("Allocating!\n");
Send_r = (struct cell *)malloc(count * (sizeof(struct cell)));
for (iter = 0; iter < count; iter ) {
Send_r[iter].aa = (double *)malloc((sAS - 1) * sizeof(double));
Send_r[iter].bb = (double *)malloc((sAS - 1) * sizeof(double));
}
Send_l = (struct cell *)malloc(count * (sizeof(struct cell)));
for (iter = 0; iter < count; iter ) {
Send_l[iter].aa = (double *)malloc((sAS - 1) * sizeof(double));
Send_l[iter].bb = (double *)malloc((sAS - 1) * sizeof(double));
}
}
void allocateInOneSendRecv(int count, int sAS) {
printf("Allocating!\n");
Send_l = malloc(count * (sizeof(*Send_l)) count *(sizeof(*Send_l) 2 * (sAS - 1) * sizeof(double)));
Send_r = malloc(count * (sizeof(*Send_r)) count *(sizeof(*Send_r) 2 * (sAS - 1) * sizeof(double)));
}
void freeSendRecv(int count) {
int iter = 0;
printf("Deallocating!\n");
free(Send_r);
free(Send_l);
}
void fillSendRecv(int count, int sAS) {
int iter = 0;
int iter2= 0;
double dummyDouble = 5.0;
printf("Filling!\n");
for (iter = 0; iter < count; iter ) {
Send_l[iter].a = dummyDouble;
Send_l[iter].b = dummyDouble;
Send_l[iter].c = dummyDouble;
for (iter2 = 0; iter2 < sAS; iter2 ) {
Send_l[iter].aa[iter2] = dummyDouble;
Send_l[iter].bb[iter2] = dummyDouble;
}
dummyDouble ;
Send_r[iter].a = dummyDouble;
Send_r[iter].b = dummyDouble;
Send_r[iter].c = dummyDouble;
for (iter2 = 0; iter2 < sAS; iter2 ) {
Send_r[iter].aa[iter2] = dummyDouble;
Send_r[iter].bb[iter2] = dummyDouble;
}
dummyDouble ;
}
}
void printSendRecv(int count, int sAS) {
int iter = 0;
printf("Printing!\n");
for (iter = 0; iter < count; iter ) {
printf("%f \n", Send_l[iter].a);
printf("%f \n", Send_l[iter].b);
printf("%f \n", Send_l[iter].c);
printf("%f \n", Send_l[iter].aa[sAS - 1]);
printf("%f \n\n", Send_l[iter].bb[sAS - 1]);
printf("%f \n", Send_r[iter].a);
printf("%f \n", Send_r[iter].b);
printf("%f \n", Send_r[iter].c);
printf("%f \n", Send_r[iter].aa[sAS - 1]);
printf("%f \n\n", Send_r[iter].bb[sAS - 1]);
}
}
uj5u.com熱心網友回復:
您當前的問題是您只能傳遞Send_l(resp. Send_r) 的起始地址。從那時起,所有記憶體都必須是連續的,并且您必須知道它的總大小并稍后將其提供給MPI_SendRecv.
但是在分配之后,您必須確保aa和bb成員被正確初始化以指向分配的記憶體塊內。
一個可能的代碼可能是:
void allocateSendRecv(int count, int subCount) {
int iter;
// total size of each struct
size_t sz = sizeof(struct cell) 2 * subCount * sizeof(double);
// one single contiguous allocation
Send_r = malloc(count * sz); // nota: never cast malloc in C language!
// per each cell make aa and bb point into the allocated memory
for (iter = 0; iter < count; iter ) {
Send_r[iter].aa = ((double*)(Send_r count)) 2 * subCount * iter;
Send_r[iter].bb = Send_r[iter].aa subCount;
}
// id. for Send_l
Send_l = malloc(count * sz);
for (iter = 0; iter < count; iter ) {
Send_l[iter].aa = ((double*)(Send_l count)) 2 * subCount * iter;
Send_l[iter].bb = Send_l[iter].aa subCount;
}
}
在這里,我首先是cell結構陣列,然后是 1 個aa陣列和每個結構 1 個bb陣列。
這足以擺脫分段錯誤......
uj5u.com熱心網友回復:
您可以使用匿名struct,但有一些注意事項:
#define CELL(n) \
struct { \
double a, b, c, aa[n], bb[]; \
}
限制是您不能按原樣使用全域變數,并且必須傳遞void *給子例程(然后在主體內強制轉換)。如果需要全域變數,只能使用宣告為的指標void *
例如
#define CELL(n) \
struct { \
double a, b, c, aa[n], bb[]; \
}
void * Send_r;
void allocateCells(int count, int sAS) {
Send_r = malloc (count * sizeof(CELL(sAS)); // no cast here
}
void fillCells(void * _cells, int count, int sAS, double dummyDouble) {
int iter = 0;
int iter2= 0;
printf("Filling!\n");
CELL(sAS) * cells = _cells;
for (iter = 0; iter < count; iter ) {
cells[iter].a = dummyDouble;
cells[iter].b = dummyDouble;
cells[iter].c = dummyDouble;
for (iter2 = 0; iter2 < sAS; iter2 ) {
cells[iter].aa[iter2] = dummyDouble;
cells[iter].bb[iter2] = dummyDouble;
}
}
int main() {
int sAS = 5;
int iter = 0;
allocate(count, sAS);
fill(Send_r, count, sAS, 5.0);
}
uj5u.com熱心網友回復:
單一的全域struct
struct cell
{
double a, b, c, *aa, *bb;
} * Send_l, *Send_r;
有點脆弱:
aa并被bb分配為陣列,double但subCount -1大小不存在。它被隱藏在代碼中。Send_l并且Send_r也是指向陣列的指標,struct cell但count大小不存在。它也隱藏在代碼中。單一struct是全球性的,它也很弱。
這使得測驗、分配或釋放資料變得困難。我將使用一些封裝留下一個C示例,您可以在MPI. 我將使用您的代碼和函式,并帶有一些面向 OOP 的知識:)
該示例包括 2 個程式和函式來序列化和反序列化資料。為了進行測驗,第一個程式將資料寫入檔案并由第二個程式讀回。同樣printSendRecv()顯示了資料寫入磁盤之前和之后的資料。
一種Cell結構
typedef struct
{
double a;
double b;
double c;
double* aa;
double* bb;
} Cell;
Send結構_
typedef struct
{
Cell l;
Cell r;
} Send;
Set結構_
typedef struct
{
unsigned count;
unsigned subCount;
Send* send;
} Set;
所以 aSet具有描述其內容所需的一切。
函式原型
Set* allocateSendRecv(size_t, size_t);
int fillSendRecv(Set*);
Set* freeSendRecv(Set*);
int printSendRecv(Set*, const char*);
使用封裝和一點RAIIfromC 你可以重寫as 的建構式allocateSendRecv()和freeSendRecv()解構式struct:
Set* allocateSendRecv(size_t count, size_t subCount)
{
// count is the number of send buffers
// subcount is the size of the arrays inside each cell
printf(
"AllocateSendRecv(count = %llu, subCount = %llu)\n", count,
subCount);
Set* nw = (Set*)malloc(sizeof(Set));
nw->count = count;
nw->subCount = subCount;
nw->send = (Send*)malloc(count * sizeof(Send));
// now that we have Send allocate the Cell arrays
for (size_t i = 0; i < count; i )
{
nw->send[i].l.aa =
(double*)malloc(subCount * sizeof(double));
nw->send[i].l.bb =
(double*)malloc(subCount * sizeof(double));
nw->send[i].r.aa =
(double*)malloc(subCount * sizeof(double));
nw->send[i].r.bb =
(double*)malloc(subCount * sizeof(double));
}
return nw;
}
Set* freeSendRecv(Set* set)
{
if (set == NULL) return NULL;
printf(
"\nDeallocating(count = %llu, subCount = %llu)\n",
set->count, set->subCount);
for (size_t i = 0; i < set->count; i )
{
free(set->send[i].l.aa);
free(set->send[i].l.bb);
}
free(set->send);
free(set);
return NULL;
}
以這種方式撰寫tst指標在對 的呼叫中無效freeSendRecv()。在這種情況下tst,分配了countandsubCount作為 2 和 5,這進入了Set.
fillSendRecv()使用增量填充值可以很容易地確定一些最終的位移。printSendRecv()接受可選訊息的字串。值在創建之前和之后列印Set。
示例:序列化和反序列化緩沖區
連載()
為了首先寫入磁盤或傳輸資料aa,bb必須擴展陣列。該示例用于使用這些值v2-out x y 4 file創建和顯示 a struct,然后寫入 if tofile
int main(int argc, char** argv)
{
char f_name[256] = {0};
if (argc < 3) usage();
strcpy(f_name, argv[3]);
size_t count = atoll(argv[1]);
size_t subCount = atoll(argv[2]);
Set* tst = allocateSendRecv(count,subCount);
fillSendRecv(tst);
printSendRecv(tst, "printSendRecv(): ");
to_disk(tst, f_name);
tst = freeSendRecv(tst);
return 0;
}
這些函式接受Set并寫入檔案:
int to_disk(Set*, const char*);
int write_cell(Cell*, const size_t, FILE*);
反序列化()
由于Set具有重新創建所需的所有內容,因此Set只需要檔案名。該示例用于v2-in file讀取資料file并將其顯示在螢屏上
int main(int argc,char** argv)
{
char f_name[256] = {0};
if (argc < 2) usage();
strcpy(f_name, argv[1]);
Set* tst = from_disk(f_name);
printSendRecv(tst, "As read from disk: ");
tst = freeSendRecv(tst);
return 0;
}
這些函式讀取檔案并回傳指向 a 的指標Set和資料:
Set* from_disk(const char*);
int read_cell(FILE*, Cell*, const size_t);
示例的輸出
這里的程式是
v2-out創建Set并寫入磁盤中的檔案v2-in讀取由創建的檔案v2-out并加載到新的Setdump.bin被創造并Set擁有count = 2和subCount = 4
PS C:\SO>
PS C:\SO> .\v2-out 2 4 dump-2-4.bin
AllocateSendRecv(count = 2, subCount = 4)
FillSendRecv()
printSendRecv(): Count is 2, subCount is 4
Set 1 of 2
l:
[a,b,c] = [ 42.001, 42.002, 42.003]
aa: 42.004 42.005 42.006 42.007
bb: 42.008 42.009 42.010 42.011
r:
[a,b,c] = [ 42.012, 42.013, 42.014]
aa: 42.015 42.016 42.017 42.018
bb: 42.019 42.020 42.021 42.022
Set 2 of 2
l:
[a,b,c] = [ 42.023, 42.024, 42.025]
aa: 42.026 42.027 42.028 42.029
bb: 42.030 42.031 42.032 42.033
r:
[a,b,c] = [ 42.034, 42.035, 42.036]
aa: 42.037 42.038 42.039 42.040
bb: 42.041 42.042 42.043 42.044
writing 'Set' to "dump-2-4.bin"
Deallocating(count = 2, subCount = 4)
PS C:\SO> .\v2-in dump-2-4.bin
read 'Set' from "dump-2-4.bin"
From disk: Count = 2, SubCount = 4
AllocateSendRecv(count = 2, subCount = 4)
new 'Set' created
As read from disk: Count is 2, subCount is 4
Set 1 of 2
l:
[a,b,c] = [ 42.001, 42.002, 42.003]
aa: 42.004 42.005 42.006 42.007
bb: 42.008 42.009 42.010 42.011
r:
[a,b,c] = [ 42.012, 42.013, 42.014]
aa: 42.015 42.016 42.017 42.018
bb: 42.019 42.020 42.021 42.022
Set 2 of 2
l:
[a,b,c] = [ 42.023, 42.024, 42.025]
aa: 42.026 42.027 42.028 42.029
bb: 42.030 42.031 42.032 42.033
r:
[a,b,c] = [ 42.034, 42.035, 42.036]
aa: 42.037 42.038 42.039 42.040
bb: 42.041 42.042 42.043 42.044
Deallocating(count = 2, subCount = 4)
2個檔案中的示例
標題v2.h
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
double a;
double b;
double c;
double* aa;
double* bb;
} Cell;
typedef struct
{
Cell l;
Cell r;
} Send;
typedef struct
{
size_t count;
size_t subCount;
Send* send;
} Set;
Set* allocateSendRecv(size_t, size_t);
int fillSendRecv(Set*);
Set* freeSendRecv(Set*);
int printSendRecv(Set*, const char*);
// helpers
Set* from_disk(const char*);
double get_next(void);
int print_cell(Cell*, size_t, const char*);
int read_cell(FILE*, Cell*, const size_t);
int to_disk(Set*, const char*);
int write_cell(Cell*, const size_t, FILE*);
檔案中的代碼v2.c
#include "v2.h"
#include <stdio.h>
#pragma pack(show)
Set* allocateSendRecv(size_t count, size_t subCount)
{
// count is the number of send buffers
// subcount is the size of the arrays inside each cell
printf(
"AllocateSendRecv(count = %llu, subCount = %llu)\n", count,
subCount);
Set* nw = (Set*)malloc(sizeof(Set));
nw->count = count;
nw->subCount = subCount;
nw->send = (Send*)malloc(count * sizeof(Send));
// now that we have Send allocate the Cell arrays
for (size_t i = 0; i < count; i )
{
nw->send[i].l.aa =
(double*)malloc(subCount * sizeof(double));
nw->send[i].l.bb =
(double*)malloc(subCount * sizeof(double));
nw->send[i].r.aa =
(double*)malloc(subCount * sizeof(double));
nw->send[i].r.bb =
(double*)malloc(subCount * sizeof(double));
}
return nw;
}
int fillSendRecv(Set* s)
{
printf("FillSendRecv()\n");
if (s == NULL) return -1;
for (size_t i = 0; i < s->count; i = 1)
{
// l
s->send[i].l.a = get_next();
s->send[i].l.b = get_next();
s->send[i].l.c = get_next();
for (size_t j = 0; j < s->subCount; j = 1)
s->send[i].l.aa[j] = get_next();
for (size_t j = 0; j < s->subCount; j = 1)
s->send[i].l.bb[j] = get_next();
// r
s->send[i].r.a = get_next();
s->send[i].r.b = get_next();
s->send[i].r.c = get_next();
for (size_t j = 0; j < s->subCount; j = 1)
s->send[i].r.aa[j] = get_next();
for (size_t j = 0; j < s->subCount; j = 1)
s->send[i].r.bb[j] = get_next();
}
return 0;
}
Set* freeSendRecv(Set* set)
{
if (set == NULL) return NULL;
printf(
"\nDeallocating(count = %llu, subCount = %llu)\n",
set->count, set->subCount);
for (size_t i = 0; i < set->count; i )
{
free(set->send[i].l.aa);
free(set->send[i].l.bb);
}
free(set->send);
free(set);
return NULL;
}
int printSendRecv(Set* s, const char* msg)
{
if (s == NULL) return -1;
if (msg != NULL) printf("%s", msg);
printf(
" Count is %llu, subCount is %llu\n", s->count,
s->subCount);
for (size_t i = 0; i < s->count; i = 1)
{
printf("\tSet %llu of %llu\n", 1 i, s->count);
print_cell(&s->send[i].l, s->subCount, "\tl:\n");
print_cell(&s->send[i].r, s->subCount, "\tr:\n");
printf("\n");
}
printf("\n");
return 0;
}
// helpers
Set* from_disk(const char* file)
{
printf("read 'Set' from \"%s\"\n", file);
FILE* in = fopen(file, "rb");
if (in == NULL) return NULL;
size_t res = 0;
size_t count = 0;
res = fread(&count, sizeof(count), 1, in);
size_t subCount = 0;
res = fread(&subCount, sizeof(subCount), 1, in);
printf("From disk: Count = %llu, SubCount = %llu\n",
count,subCount);
Set* nw = allocateSendRecv(count, subCount);
if (nw == NULL)
{
fclose(in);
return NULL; // could not alloc
}
printf("new 'Set' created\n");
nw->count = count;
nw->subCount = subCount;
// so we have the exact structure to hold ALL data
for (size_t i = 0; i < nw->count; i = 1)
{
read_cell(in, &nw->send[i].l, nw->subCount);
read_cell(in, &nw->send[i].r, nw->subCount);
}
fclose(in);
return nw;
}
double get_next(void)
{
static double ix = 42.;
ix = .001;
return ix;
}
int print_cell(Cell* cell, size_t sz, const char* msg)
{
printf(
"%s\t[a,b,c] = [.3f,.3f,.3f]\n", msg,
cell->a, cell->b, cell->c);
printf("\taa: ");
for (size_t j = 0; j < sz; j = 1)
printf(".3f ", cell->aa[j]);
printf("\n\tbb: ");
for (size_t j = 0; j < sz; j = 1)
printf(".3f ", cell->bb[j]);
printf("\n\n");
return 0;
}
int read_cell(FILE* in, Cell* cell, const size_t size)
{
if (in == NULL) return -2;
if (cell == NULL) return -1;
size_t res = 0;
// a,b,c,aa,bb
res = fread(&cell->a, 1, 3 * sizeof(double), in);
res = fread(cell->aa, 1, size * sizeof(double), in);
res = fread(cell->bb, 1, size * sizeof(double), in);
return 0;
}
int to_disk(Set* set, const char* file)
{
printf("writing 'Set' to \"%s\"\n", file);
FILE* out = fopen(file, "wb");
if (out == NULL) return -1;
size_t res = 0;
res = fwrite(&set->count, sizeof(set->count), 1, out);
res = fwrite(&set->subCount, sizeof(set->subCount), 1, out);
for (size_t i = 0; i < set->count; i = 1)
{
write_cell(&set->send[i].l, set->subCount, out);
write_cell(&set->send[i].r, set->subCount, out);
}
fclose(out);
return 0;
}
int write_cell(Cell* cell, const size_t size, FILE* out)
{
if (cell == NULL) return -1;
if (out == NULL) return -2;
size_t res = 0;
// a,b,c, aa, bb
res = fwrite(&cell->a, 1, 3 * sizeof(double), out);
res = fwrite(cell->aa, 1, size * sizeof(double), out);
res = fwrite(cell->bb, 1, size * sizeof(double), out);
//printf("write_cell(): %llu bytes written to disk\n", res);
return 0;
}
main()上面的 2 個例子在文本中
鑄造回報malloc()
是的,我總是malloc()像我和許多其他人一樣不喜歡任何暗示的回報。并且還因為malloc()接受任何計算結果為大小的運算式,并且在運算式中并不總是說明該區域。很多時候,程式為許多結構分配資料,其中一些是封閉的。這個小程式有3個。所以使用演員表可以提醒程式員程式打算分配什么,并且可以避免很多錯誤,因為多次表達不足以顯示什么是什么。
關于這件事malloc()并且 cast 來自 C-FAQ,這是一個從未更新過的舊東西,它是 usenet 上所有 2000 年之前的文章的匯編。即使在那個時候,人們也在那里寫了關于 CAST 指標的可能原因。(C-FAQ)[https://c-faq.com/malloc/sd3.html] 中支持強制轉換的原因之一是它可以提醒程式員忘記使用includefor stdlib.h。我是認真的:
Suppose that you call malloc but forget to #include <stdlib.h>.
The compiler is likely to assume that malloc is a function
returning int, which is of course incorrect, and will lead to trouble
Therefore, the seemingly redundant casts are used by people who are
(a) concerned with portability to all pre-ANSI compilers, or
(b) of the opinion that implicit conversions are a bad thing.
我會添加我上面描述的原因。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/518370.html
標籤:C结构mpi连续的
上一篇:訪問字符指標
下一篇:如何在C語言中將數字視為值1?
