Go 可以在页面级别处理内存吗?

Can Go work with memory at the page level?

我是一名 Go 开发人员,正在阅读一本名为 Database Internals 的书。作者广泛讨论了在非常低的级别(特别是在页面级别)使用内存。

当我尝试构建自己的数据库时,我查看了 Go 文档和其他文章,以讨论在这个级别上使用内存,但是似乎没有任何内容谈论在特定内存页中使用数据。

我的问题是:

Go 可以做到这一点吗?

如果 Go 做不到,这是 C 或 C++ 能做的吗?

如果 Go、C 或 C++ 的 none 可以做到这一点,也许我不理解作者在页面级别处理内存的意思,我应该如何考虑它?

如有必要,请让我知道需要添加到问题中的任何进一步细节。

摘自本书

上下文是我们正在谈论在页面中实现 B-Trees 和分组节点。这可以在第 2 章标题为“On-Disk 结构”的部分找到

PAGED BINARY TREES Laying out a binary tree by grouping nodes into pages, as Figure 2-6 shows, improves the situation with locality. To find the next node, it’s only necessary to follow a pointer in an already fetched page. However, there’s still some overhead incurred by the nodes and pointers between them. Laying the structure out on disk and its further maintenance are nontrivial endeavors, especially if keys and values are not presorted and added in random order. Balancing requires page reorganization, which in turn causes pointer updates.

它还提供了这张图片:

我可能遗漏了什么, 但是使用物理内存(与虚拟内存相反)不是语言功能,而是 OS.
所以你的问题的答案是:是的,Go 也可以做到这一点。

每个操作系统都提供了使用实际地址的方法space, 这些服务需要更高的权限,并以 Driver/Kernel-Module 的形式出现 并将此访问权限用于内存映射 IO 或预分配物理内存块(在 MMI 将其用作分页内存之前)。

我猜想在系统加载时(通过驱动程序)预分配内存块并使用它是你想要做的。

如果您打算访问“物理内存”上的“虚拟内存”,我会劝阻您并说这是一个极其脆弱和脆弱的机制,除非您发现这样做有巨大的优势,否则我看不到走这条路的任何理由。

这本书在谈论“磁盘上结构”。 Page 在此上下文中仅表示 一个数据块 。磁盘访问在扇区或簇中进行,因此数据应针对局部性进行优化,以便尽可能适合这些块。数据库文件设计的挑战是将数据的时间局部性转化为空间局部性。

但这个概念也适用于 RAM。在用户 space 虚拟内存中 在页面级别 工作仅仅意味着了解底层内存架构,并针对它进行优化。

在 x86 上,user-space 虚拟内存以 4 KB* 的页面组织。

这意味着在处理大量数据时,值得使用 4 KB 的倍数并与 4 KB 对齐的内存区域。

在 C 中有很多方法可以实现,例如aligned_alloc.

但 Go 更进一步 - 它已经至少在页面大小上对齐大型数组(在 Mac、BSD、Linux 和 Windows 上)。

func main() {
    buf := make([]byte, 1024*1024)
    fmt.Printf("%p\n", unsafe.Pointer(&buf[0]))
}

将打印如下内容:

0xc000180000

如果您遇到不符合要求的实现,您始终可以通过分配 PageSize-1 额外字节“手动”对齐 byte 切片,然后跳过开头不是 PageSize 倍数的字节,使用对齐公式:

offset = (alignment - base) & (alignment - 1)

func main() {
    buf := make([]byte, 1024*1024 + 4096 - 1)
    base := unsafe.Pointer(&buf[0])
    offset := (4096 - uintptr(base)) & (4096 - 1)
    aligned := buf[offset:]
    fmt.Printf("base   : %p\n", unsafe.Pointer(&buf[0]))
    fmt.Printf("aligned: %p\n", unsafe.Pointer(&aligned[0]))
}

(想不出上面会打印不同值的平台)


* 某些平台支持 2MB 和 1GB 页面的大页面。 Go 在可用时自动使用它们。