使用荒谬的内存量来实现堆栈 – C.
我在C中编写了一个min-heap的实现,作为Dijkstra算法的一部分.我已经完成了所有细节,我的测试程序通过了valgrind测试,但它在这个过程中分配了大量的内存.最后的测试是在INT_MAX的网格上由INT_MAX(坐标只是整数),我测试时得到SIGXCPU错误.即使我只是将16k位置插入队列然后删除所有内容,它仍然需要很长时间并分配超过8 MB.当我在巨大的网格测试用例上运行它时,在我手动退出之前它可以达到500 MB.会发生什么事?这是我的代码的一部分:
struct position { int x; int y }; typedef struct elt { int priority; int distance; struct position p; } *Elt; typedef struct heap { int size; int capacity; Elt *elts; } *Heap; void heap_insert(Heap h,Elt e,int *counter) { if(h->capacity < (h->size + 2)) { h->elts = realloc(h->elts,h->capacity * sizeof(Elt) * 2); h->capacity *= 2; } h->elts[h->size] = malloc(sizeof(*Elt)); elt_assign(h->elts[h->size],e); h->size++; heapify(h->size,h->elts); *counter = *counter + 1; } 我的所有其他功能都是一次性,功能性或根本不进行内存管理.在这种情况下,初始大小为64,但是从1024开始我的效果相同.我也尝试限制队列的大小,但无济于事.我很确定这不是我的堆码代码,但这只是为了以防万一 static void floatDown(int n,Elt *a,int pos) { Elt x = malloc(sizeof(struct elt)); elt_assign(x,a[pos]); for(;;) { if(Child(pos,1) < n && a[Child(pos,1)]->priority < a[Child(pos,0)]->priority) { if(a[Child(pos,1)]->priority < x->priority) { elt_assign(a[pos],a[Child(pos,1)]); pos = Child(pos,1); } else { break; } } else if(Child(pos,0) < n && a[Child(pos,0)]->priority < x->priority) { elt_assign(a[pos],0)]); pos = Child(pos,0); } else { break; } } elt_assign(a[pos],x); free(x); } static void heapify(int n,Elt *a) { for(int i = n - 1; i >= 0; i--) { floatDown(n,a,i); } } 任何帮助将非常感激. 解决方法
这是我的工作理论.我愿意发现我错了,但没有剩下的代码,我无法检测,运行和测试它.
… struct heap {… Elt * elts的间接; } …当typedef struct elt {…} * Elt;正在节省复制4个整数的成本并用复制1个指针替换它,但复制速度很快,而且只发生log2(N)次. 相反,每个结构都是单独的malloc.如果没有挖掘来找到malloc’d块的实际大小,我们可以估计平均会浪费N / 2 sizeof(struct elt)(实际上,我认为它在我的机器上更糟). 它还可能创建不连续的内存块(通过在较大的块之间放置小块),因此realloc必须始终分配更大的块,因此重用以前的块将更加困难.在这种特殊情况下,我认为这与内部碎片或malloc的大量调用造成的浪费无关. 它也可能会创建一个“缓存破坏者”.实际值正在整个内存中传播,并且由于malloc’d struct elt块的内部碎片,缓存行相对稀疏. 所以替换: typedef struct elt { int priority; int distance; struct position p; } *Elt; typedef struct heap { int size; int capacity; Elt *elts; } *Heap; 同 typedef struct elt { int priority; int distance; struct position p; } Elt; // no longer a pointer typedef struct heap { int size; int capacity; Elt *elts; } *Heap; 并改变: void heap_insert(Heap h,h->elts); *counter = *counter + 1; } 至 void heap_insert(Heap h,h->capacity * sizeof(Elt) * 2); h->capacity *= 2; } h->elts[h->size] = e; // no longer need to malloc h->size++; heapify(h->size,h->elts); *counter = *counter + 1; } 因此,用于保存堆的malloc’d / realloc的内存量应大约为2 * N * sizeof(struct elt).函数/宏elt_assign可能会更改为隐藏其他更改. 然后通过改变来进一步减少malloc’ing的数量: static void floatDown(int n,a[pos]); ... elt_assign(a[pos],x); free(x); } 至 static void floatDown(int n,int pos) { Elt x = a[pos]; ... a[pos] = x; } 这应该进一步减少malloc’ed和free’d的内存量. 本质上,应该只有(大约)log2(N)调用realloc. realloc可能还有更好的机会扩展现有块而不是副本. 编辑: heap_insert中存在比内存分配更大的问题: void heap_insert(Heap h,int *counter) { ... heapify(h->size,h->elts); ... } 对插入堆中的每个元素调用heapify,即heapify被调用N次. heapify是: static void heapify(int n,i); } } 对于插入的每个元素,到目前为止,它会调用堆中的每个元素.因此heap_insert具有大约(N ^ 2)/ 2(即O(N ^ 2))运行时间的运行时间. 我相信heap_insert应该为它添加到堆中的每个元素使用floatDown,而不是heapify. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |