C++ 计算文件中的矩形数
我试图使用MPI c/c++和OpenMP c/c++库来计算给定数据集中有多少个矩形。 该数据集是一个1s和0s的文本文件,它非常大,像10000x100000行0s和1s,但为了简单起见,我将提供该数据集中的数据示例 我最初考虑将数据放入一个整数2d数组,这里是我的数据集的样子 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 我应该得到4个矩形的坐标C++ 计算文件中的矩形数,c++,c,parallel-processing,mpi,openmp,C++,C,Parallel Processing,Mpi,Openmp,我试图使用MPI c/c++和OpenMP c/c++库来计算给定数据集中有多少个矩形。 该数据集是一个1s和0s的文本文件,它非常大,像10000x100000行0s和1s,但为了简单起见,我将提供该数据集中的数据示例 我最初考虑将数据放入一个整数2d数组,这里是我的数据集的样子 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1
我该如何解决这个问题?如果我想使用mpi/openmp库来提高效率,我需要在线程上划分矩阵吗?我认为下面的方法可以解决这个问题。 请注意,如果我没有错的话,这个(新的,请参阅历史)算法应该缩放为O(N)
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
//--some helper functions and structs--
typedef struct Line_s {
long x0;
long nx;
long y0;
} Line;
typedef struct LineVect_s {
Line *lines;
long size;
long capacity;
} LineVect;
LineVect* newLineVect(void) {
LineVect *vect = malloc(sizeof(LineVect));
vect->lines = malloc(sizeof(Line));
vect->size = 0;
vect->capacity = 1;
return vect;
}
void deleteLineVect(LineVect* self) {
free(self->lines);
free(self);
}
void LineVect_pushBack(LineVect* self, Line line) {
self->size++;
if (self->size > self->capacity) {
self->capacity *= 2;
self->lines = realloc(self->lines, self->capacity * sizeof(Line));
}
self->lines[self->size - 1] = line;
}
typedef struct Block_s {
long x0, y0;
long nx, ny;
char val;
} Block;
typedef struct BlockVect_s {
Block *blocks;
long size;
long capacity;
} BlockVect;
BlockVect* newBlockVect(void) {
BlockVect *vect = malloc(sizeof(BlockVect));
vect->blocks = malloc(sizeof(Block));
vect->size = 0;
vect->capacity = 1;
return vect;
}
void deleteBlockVect(BlockVect* self) {
free(self->blocks);
free(self);
}
void BlockVect_pushBack(BlockVect* self, Block block) {
self->size++;
if (self->size > self->capacity) {
self->capacity *= 2;
self->blocks = realloc(self->blocks, self->capacity * sizeof(Block));
}
self->blocks[self->size - 1] = block;
}
LineVect** find_lines(char* data, long nx, long ny) {
LineVect **slices = malloc(ny * sizeof(LineVect*));
//essentially each y-slice is independent here
#pragma omp parallel for
for (long y = 0; y < ny; y++) {
slices[y] = newLineVect();
char prev_val = 0;
long xstart = 0;
for (long x = 0; x < nx; x++) {
char val = data[nx*y + x];
if (val == 1 && prev_val == 0) {
xstart = x;
} else if (val == 0 && prev_val == 1) {
Line line;
line.y0 = -1;
line.x0 = xstart;
line.nx = x - xstart;
// printf("Found line at y=%d from x=%d to %d\n", y, xstart, x-1);
LineVect_pushBack(slices[y], line);
}
if (val == 1 && x == nx-1) {
Line line;
line.y0 = -1;
line.x0 = xstart;
line.nx = x - xstart + 1;
// printf("Found line at y=%d from x=%d to %d\n", y, xstart, x);
LineVect_pushBack(slices[y], line);
}
prev_val = val;
}
}
return slices;
}
BlockVect* find_blocks(LineVect** slices, long ny) {
BlockVect* blocks = newBlockVect();
//for each line
for (long y = 0; y < ny; y++) {
LineVect* slice = slices[y];
long l2 = 0;
for (long l = 0; l < slice->size; l++) {
Line *line = slice->lines + l;
if (line->y0 == -1) {
line->y0 = y;
}
char match = 0;
//check next slice if there is any
if (y != ny-1) {
//check all the remaining lines in the next slice
if (l2 < slices[y+1]->size) {
Line *line2 = slices[y+1]->lines + l2;
//note that we exploit the sorted nature of the lines (x0 must increase with l2)
for (; l2 < slices[y+1]->size && line2->x0 < line->x0; l2++) {
line2 = slices[y+1]->lines + l2;
}
//matching line2 found -> mark it as same block (y0 cascades)
if (line->x0 == line2->x0 && line->nx == line2->nx) {
match = 1;
line2->y0 = line->y0;
}
}
}
//current line didn't find a matching line hence store it (and all the previously collected lines) as a block
if (match == 0) {
Block block;
block.x0 = line->x0;
block.nx = line->nx;
block.y0 = line->y0;
block.ny = y - line->y0 + 1;
BlockVect_pushBack(blocks, block);
}
}
}
return blocks;
}
#define Nx 30000
#define Ny 30000
char * write_blocks(const BlockVect* blocks) {
char * data = calloc(Ny, Nx*sizeof(char));
for (long i = 0; i < blocks->size; i++) {
Block *block = blocks->blocks + i;
for (long y = block->y0; y < block->y0 + block->ny; y++) {
for (long x = block->x0; x < block->x0 + block->nx; x++) {
data[Nx*y + x] = 1;
}
}
}
return data;
}
int main() {
struct timeval t0;
gettimeofday(&t0, NULL);
// char data[Ny][Nx] = {
// {0, 0, 0, 0, 1, 1, 1, 0},
// {0, 0, 0, 0, 1, 1, 1, 0},
// {0, 1, 1, 1, 0, 0, 0, 1},
// {0, 1, 1, 1, 0, 0, 0, 1},
// {0, 1, 1, 1, 0, 1, 1, 0},
// {0, 0, 0, 0, 0, 1, 1, 0},
// {0, 1, 0, 1, 1, 1, 1, 1},
// {0, 0, 0, 1, 1, 1, 1, 1}
// };
srand(t0.tv_usec);
char * input = calloc(Ny, Nx*sizeof(char));
printf("Filling...\n");
for (long y = 0; y < Ny; y++) {
for (long x = 0; x < Nx; x++) {
input[Nx*y + x] = rand() % 10 != 0; //data[y][x];
}
}
printf("Computing...\n");
struct timeval t;
struct timeval t_new;
gettimeofday(&t, NULL);
LineVect** slices = find_lines(input, Nx, Ny);
gettimeofday(&t_new, NULL);
printf("Finding lines took %lu milliseconds\n", (t_new.tv_sec - t.tv_sec)*1000 + (t_new.tv_usec -t.tv_usec) / 1000);
gettimeofday(&t, NULL);
BlockVect* blocks = find_blocks(slices, Ny);
gettimeofday(&t_new, NULL);
printf("Finding blocks took %lu milliseconds\n", (t_new.tv_sec - t.tv_sec)*1000 + (t_new.tv_usec -t.tv_usec) / 1000);
long n_lines = 0;
for (long y = 0; y < Ny; y++) {
n_lines += slices[y]->size;
deleteLineVect(slices[y]);
}
free(slices);
printf("Done computation of %ld lines and %ld blocks\n", n_lines, blocks->size);
char * output = write_blocks(blocks);
deleteBlockVect(blocks);
char pass = 1;
for (long y = 0; y < Ny; y++) {
for (long x = 0; x < Nx; x++) {
if (input[Nx*y + x] != output[Nx*y + x]) {
printf("ERROR at x=%ld and y=%ld!\n", x, y);
pass = 0;
}
}
}
printf("Plausibility check %s\n", pass ? "PASSED" : "FAILED");
//free all the rest
free(input);
free(output);
gettimeofday(&t_new, NULL);
printf("Whole run took %.3lf seconds\n", (t_new.tv_sec - t0.tv_sec) + (t_new.tv_usec -t0.tv_usec) * 1e-6);
}
OMP_NUM_线程数=2
填充。。。
计算。。。
查找行花费了1725毫秒
查找块花费了2019毫秒
完成81027281条线路和80757086块的计算
合理性检查通过
全程15.392秒
在我的电脑上(英特尔(R)核心(TM)i7-6700CPU@3.40GHz,4个物理核心): OMP_NUM_线程数=1
填充。。。
计算。。。
查找行花费了3656毫秒
查找块花费了1631毫秒
完成81024019条线和80754472块的计算
合理性检查通过
全程耗时13.611秒
OMP_NUM_线程数=4
填充。。。
计算。。。
查找行花费了1007毫秒
查找块花费了1637毫秒
完成81036637条线和80766687块的计算
合理性检查通过
整个跑步过程耗时11.055秒
OMP_NUM_线程数=8
填充。。。
计算。。。
查找行花费了812毫秒
查找块花费了1655毫秒
完成了81025147条线和80754509个块的计算
合理性检查通过
整个跑了11.137秒
所以看起来,平行部分的缩放(寻线)相当不错。
运行的大部分时间是通过随机数(填充)实际生成样本数据。相比之下,查找RECT的速度非常快。如果您真的想使用OpenMPI或类似地使用openmp执行分布式内存方法,我建议如下:将域分解为子域,以便每个线程/进程为其子域运行上述代码。最后,您将需要一些通信,其中只有那些接触子域边界的块才能在线程/进程团队中交换。然而,这需要大量的代码,尤其是使用MPI。祝你好运!您好,我可以知道s代表什么吗?我知道POSIX的t只是一个表示类型的约定,我猜s表示它是一个结构,对吗?如果您不知道,我想通过电子邮件向您询问一些关于所提供代码的问题mind@AbdullahAlSulaimanU只是为了使typedef和struct在形式上保持不同,也就是说,它可以省略,但它有助于某些IDE区分typedef和struct。我不会在这里透露我的邮箱地址,sry。你可以在这里问所有的问题。我以为你想要一场公开辩论。好吧,我的问题是find_blocks算法是如何工作的,根据我的理解,你首先尝试找到连续的1行,然后将其传递给函数find_blocks,但当我试图理解find_block时,有点困惑它实际上是如何找到传递的行是否确实是一个块的(表示它是一个矩形)我真的很感谢你在代码方面所做的工作,我只是想了解其中的每一部分,这样我就可以通过MPI/OpenMPI实现代码的并行化。答案中包含了一个新代码,它应该执行得更快,而且可读性更高。我希望你能遵循这些想法。我强烈建议你用笔和纸做一个适当的练习r理解。它在您的机器上如何扩展?