【发布时间】:2016-02-06 01:03:00
【问题描述】:
我希望关于如何处理我即将进行的设计的一些高级建议。
解决我的问题的直接方法将导致数以百万计的指针。在 64 位系统上,这些可能是 64 位指针。但就我的应用程序而言,我认为我需要的不仅仅是 32 位地址空间。但是,我仍然希望系统利用 64 位处理器算法(假设这是我在 64 位系统上运行所得到的)。
更多背景
我正在实现一个树状数据结构,其中每个“节点”包含一个 8 字节的有效负载,但还需要指向四个相邻节点(父节点、左子节点、中间子节点、右子节点)的指针。在使用 64 位指针的 64 位系统上,这相当于 32 个字节,仅用于将 8 字节有效负载链接到树中——“链接开销”为 400%。
数据结构将包含数百万个这样的节点,但除此之外我的应用程序不需要太多内存,因此所有这些 64 位指针似乎都很浪费。该怎么办?有没有办法在 64 位系统上使用 32 位指针?
我考虑过
以某种方式将有效负载存储在数组中,以便索引暗示(并且暗示)“树地址”,并且可以通过对该索引的简单算术计算给定索引的邻居。不幸的是,这需要我根据树的最大深度来调整数组的大小,这是我事先不知道的,并且由于较低级别中的空节点元素可能会导致更大的内存开销,因为并非树的所有分支去同样的深度。
将节点存储在一个足以容纳所有节点的数组中,然后使用索引而不是指针来链接邻居。 AFAIK 这里的主要缺点是每个节点都需要数组的基地址才能找到它的邻居。所以他们要么需要存储它(一百万次以上),要么需要在每个函数调用中传递它。我不喜欢这样。
假设所有这些指针的最高有效 32 位为零,如果不是,则抛出异常,并仅存储最低有效 32 位。因此可以按需重构所需的指针。系统很可能使用超过 4GB,但进程永远不会。我只是假设指针从进程基地址偏移并且不知道这在通用平台(Windows、Linux、OSX)上有多安全(如果有的话)。
存储 64 位
this和指向邻居的 64 位指针之间的差异,假设该差异将在int32_t的范围内(并抛出 if它不是)。然后任何节点都可以通过将该偏移量添加到this来找到它的邻居。
有什么建议吗?关于最后一个想法(我目前认为这是我最好的候选人),我可以假设在使用少于 2GB 的进程中,动态分配的对象将在 2GB 范围内吗?或者根本不需要?
【问题讨论】:
-
速度有问题吗?如果没有,您可以编写自己的内存分配函数来处理 32 位指针。为此,您可以在内存中保留一个存储数据的区域,存储该空间的地址并使用 32 位(甚至 16 位)地址在该空间内工作。如果你能保证这个进程永远不会超过 2GB,你可以保留这么多的数量......而且每个进程都有自己的地址空间,从 0x00 开始。所以即使系统会占用更多的内存,你的进程也总是从 0x00 地址开始。
-
@Nidhoegger 这个想法似乎遇到了与我的问题中的第二个类似的问题。要让节点取消引用这些指针,它需要基地址,或指向执行取消引用的某个对象的 64 位指针。对吗?
-
您需要编写自己的 deref 函数。给它一个 32 位地址,你的函数会添加基地址到它
-
我可以假设在一个使用少于 2GB 的进程中,动态分配的对象将在 2GB 范围内吗? 通常不会,不会。在 Linux 上,如果您想要该保证,则需要
mmap(..., MAP_32BIT, ...)。在实践中,它恰好适用于 Linux+gcc 上不断增长的std::vector的中等大小分配(~18kiB),但我不想假设关于malloc的任何事情,特别是对于大型分配(如 2MiB 或更多 = 多个大页面)。见this super-hacky microbenchmark with 32-bit pointers zero-extending to 64-bit
标签: c++ pointers memory-management cross-platform 32bit-64bit