C++ 计算文件中的矩形数

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 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个矩形的坐标

  • 右上角的一个(2 x 3矩形)
  • 左边的那个(3x3矩形)
  • 两个重叠的矩形
  • 每个矩形的坐标是:左上角和右下角


    我该如何解决这个问题?如果我想使用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理解。它在您的机器上如何扩展?