【问题标题】:What is the most efficient way to store a series of values with unknown size?存储一系列未知大小的值的最有效方法是什么?
【发布时间】:2011-03-01 10:16:50
【问题描述】:

我有一个生成 size_t 值的函数(例如,名为 next_entity)。该函数充当生成器,即在每次调用时生成一个新值,最后返回 0 作为标记。

在另一个调用next_entity 的函数中,我需要将值存储在某处。在收到哨兵之前我不知道值的数量,所以我不能malloc 或在它们到来之前静态分配一个数组来包含这些值。

问题是,在哨兵到来之后,我唯一需要对值做的就是将它们保存到文件中,但不能重复,即每个值只能出现一次。

之后,不再需要这些值。

我尝试在迭代期间使用glib.h 中的GHashTable 将值存储为键,但GHashTable 的问题是传递给函数 g_hash_table_insert 的键的指针必须在迭代期间保持活动状态哈希表的生命周期,所以我必须为每个新值做malloc(sizeof(size_t))

它可以工作,但似乎效率很低,因为malloc 很耗时。

有没有更好的方法来做到这一点?

如果需要,我可以发布真实代码,但我不认为问题出在代码上。

任何帮助将不胜感激,在此先感谢您!

【问题讨论】:

  • next_entity 生成什么样的值?样本值会有所帮助。如果它可以被视为某个范围内的整数,我有一个想法,但不适用于任何泛型类型。
  • 321681232;321682368;321681472;326130336;326165232;325443504; 321680432;324998096;320391552;326178192;326178160;321680208; 325442128;324791472;326130304;326182544;325030320;326160816; 321680880;325442192;
  • @sanjeevakumar-hiremath 你的意思是位数组吗?

标签: c arrays malloc hashtable glib


【解决方案1】:

如果您的数据大小不是千兆字节,您可以使用动态数组,每次空间不足时,您都会使用realloc()ing 将其大小翻倍。使用此策略只需进行 log(N) 次重新分配。

例如,在 C++ 中,很多 std::vector 实现通常就是这样做的。

【讨论】:

  • 感谢您的回答。有什么方法可以评估限制吗?来自 glib 的 GArray 会这样吗?
  • @Pupkov-Zadnij 在第二个想法中,我从我的答案中删除了这个。从表面上看,通常的实现依赖于系统未能分配内存。此时,您尝试处理内存不足错误或被操作系统(Linux)杀死。您可能应该处理应用程序中的任何限制。
  • @Pupkov-Zadnij 查看 glib 源代码,GArray 似乎采用了相同的策略。
  • 您如何看待 Jim Balter 的回答?
【解决方案2】:

其他人建议重新分配。取而代之的是,考虑使用 malloced 块的链表,其中每个块包含一个较大的数字数组;当你填满一个块时,分配另一个块并将前一个链接到它......或者将它链接到前一个块,然后在输出它们之前反转列表。关键是您不需要将值存储在连续内存中,因此您不需要使用 realloc。 realloc 复制前一个数组,您可以避免这种情况,并且对于非常大的数组来说这会很慢,如果找不到足够大的连续块,它甚至可能会耗尽内存,而分配单个块则更有弹性。缺点是管理链表需要更多的工作。

==== 编辑 GHashTable 用法:

将您的值存储到数组中并将该元素的地址传递给哈希例程......如果它不存在,则推进数组指针。要输出值,只需从哈希表中枚举键。仅在解除分配它们时才需要数组的链表。如果这就是程序所做的全部,那么你甚至不需要维护一个链表;您可以根据需要分配数组,当您的程序退出时,它们都会被释放。

【讨论】:

  • 我喜欢这个(+1)。但是,您将如何消除多个数字块中的重复项?
  • @Pupikov-Zadnij -- 我已经编辑了这个,添加了更多信息,因为你选择了它。如果您需要有关管理此类链接列表的更多信息,请告诉我。
  • @pmg 我忽略了的好点......但重新分配的数组也存在同样的问题。为了避免重复,OP 确实需要一个哈希表或搜索树。最好使用可用的软件包。
  • @pmg -- 我使用 GHashTable 来消除重复项,问题是它无法 存储 值 @Jim Balter -- 提前谢谢你,不是t GSList from glib.h 在这种情况下够了吗?
  • @Pupkov-Zadnij 我不记得 GSList 的详细信息,但是从您上面和这里的描述来看,听起来像是使用它来消除重复项,但将值存储在我的链表或重新分配的列表中数组会解决你的问题。
【解决方案3】:

哈希表中的键是 void*,并且 void* 始终至少与 size_t 一样大。

您需要做的就是,使用 g_hash_table_new(NULL,NULL) 代替 malloc(sizeof(size_t)) 来使用 g_direct_hash 作为哈希方法。然后这样做:

g_hash_table_replace(table, GSIZE_TO_POINTER(value), GSIZE_TO_POINTER(value))

要遍历键,请使用 GPOINTER_TO_SIZE 返回 size_t。

您始终可以对任何整数类型执行此操作,而不是对其进行 malloc'ing。 (适当时使用 GINT_TO_POINTER、GUINT_TO_POINTER,而不是 GSIZE_TO_POINTER)

【讨论】:

  • 是的,效果很好!非常感谢您,您的回答绝对是迄今为止最有用的。我只是不知道 g_hash_table_new(NULL,NULL) 启用 g_direct_hash 作为哈希方法。
  • 唯一错误的是:GPOINTER_TO_SIZE 实际上转换为gsize,而不是size_t,并且这些类型的大小不同。我使用了简单的类型转换,例如 (void*)value(size_t)key
  • gsize 和 size_t 的大小不应该不同......如果是这样,那就是 glib 中的一些错误或其他东西。但是,是的,只是演员表在这里可能很好,glib 宏比实际需要的更偏执/面向未来。
  • 嗯,也许,问题是我有 x86_64 gcc 和(也许)i386 和 x86_64 glib...所以,glib 标头可能适用于 i386,我敢肯定。
【解决方案4】:

通常的方法是将使用的空间乘以一个常数 (2, 1.69, 1.618, 1.5, ...)。

我喜欢黄金比例:)

arr = malloc(elems * sizeof *arr);
{
    /* ... */
    elems = elems * 13 / 8; /* approximate golden ratio */
    tmparr = realloc(arr, elems * sizeof *arr);
    if (tmparr == NULL) /* deal with error */;
    arr = tmparr;
    /* ... */
}
free(arr);

【讨论】:

  • 看起来很棒,但黄金配给的魔力是什么?例如,为什么乘以 13/8 而不是乘以 8/13 更好?
  • 嗯......要增加数组,您必须乘以大于 1 的数字 :)
  • 黄金配给并不比2更神奇。我确信有一个适合一般用途的最佳被乘数(我不知道它是什么)。这个想法是尽量减少realloc 调用的数量尽量减少“浪费”的内存。正如我所说:我只是喜欢黄金比例。
  • :) :) 我的意思是——为什么是 13/8 而不是任何其他大于 1 的数字?
  • 黄金比例的魔力。在它下面你可以重用之前释放的内存来填充当前请求,在它上面你不能(数学推导——用法语文本——在这里:bourguet.org/v2/cs/realloc;请注意,你越接近黄金比例,在可能之前需要更多的调整大小)。 13/8 刚好在黄金比例之上,所以你永远不能重用释放的内存来填满请求。个人比较喜欢 1.5,只需要加一半的 want 就可以了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 2022-09-28
  • 1970-01-01
  • 2017-07-19
  • 2016-01-08
相关资源
最近更新 更多