我正在编写一份 C 语言实现的最小堆作为 Dijkstra 算法的一部分。我已经明确了所有细节,我的测试程序通过了 valgrind 测试,但在过程中它分配了荒谬的大量内存。最后一个测试是在一个由 INT_MAX 乘以 INT_MAX 的网格上进行的(坐标只是整数),当我进行测试时会收到 SIGXCPU 错误。即使我只是向队列中插入 16k 个位置然后删除所有内容,它仍然需要很长时间并分配超过 8MB 的内存。当我在巨大的网格测试用例上运行它时,它可以达到 500MB,在我手动退出之前。可能出了什么问题?这是我的代码的一部分:
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], a[Child(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);
}
}
非常感谢您的帮助。
elt_assign
方法在这里也会很有用。 - Makotox = malloc...
导致内存碎片化,因此我想知道heap_insert中的每个realloc是否以某种低效的方式分配了全新的内存块。还要计算每个malloc和realloc的时间,并打印出时间,也许显示指数级减速。将Elt x = malloc
替换为typedef struct elt e = a[pos];
。 - gbulmertypedef struct elt e = a[pos];
应该是struct elt e = a[pos];
。在 floatDown 中将Elt x = malloc...
更改为struct elt e = a[pos];
可能会显著减少 malloc 和 free 的空间量;如果 floatdown 被调用 (N.log N) 次,那么这是相当多的 malloc+free 调用。 - gbulmer