golang NUMA感知编程工具插件库numa的使用
golang NUMA感知编程工具插件库numa的使用
NUMA是一个用Go编写的实用程序库,它帮助我们编写一些NUMA感知的代码。
基本使用示例
以下是一个使用numa库的完整示例demo:
package main
import (
"fmt"
"github.com/lrita/numa"
)
// 定义一个结构体,包含填充字节使其达到页大小
type object struct {
X int
_ [...]byte // 填充到页大小的padding
}
// 创建与CPU数量相同的对象数组
var objects = make([]object, numa.CPUCount())
func main() {
// 获取当前CPU和NUMA节点信息
cpu, node := numa.GetCPUAndNode()
fmt.Printf("Running on CPU: %d, NUMA node: %d\n", cpu, node)
// 为当前CPU对应的对象赋值
objects[cpu].X = 42
// 打印所有NUMA节点信息
for i := 0; i < numa.NodeCount(); i++ {
fmt.Printf("NUMA node %d CPUs: %v\n", i, numa.NodeToCPUs(i))
}
// 设置当前goroutine的CPU亲和性
err := numa.SetAffinity([]int{cpu})
if err != nil {
fmt.Println("SetAffinity error:", err)
}
}
主要功能说明
numa.CPUCount()
- 获取系统CPU核心数量numa.GetCPUAndNode()
- 获取当前goroutine运行的CPU和NUMA节点numa.NodeCount()
- 获取NUMA节点数量numa.NodeToCPUs(node int)
- 获取指定NUMA节点上的CPU列表numa.SetAffinity(cpus []int)
- 设置当前goroutine的CPU亲和性
高级使用示例
package main
import (
"fmt"
"runtime"
"github.com/lrita/numa"
)
func main() {
// 获取系统信息
fmt.Println("CPU count:", numa.CPUCount())
fmt.Println("NUMA node count:", numa.NodeCount())
// 为每个NUMA节点创建专门的工作goroutine
for node := 0; node < numa.NodeCount(); node++ {
go func(n int) {
// 设置goroutine在指定NUMA节点的CPU上运行
cpus := numa.NodeToCPUs(n)
if len(cpus) > 0 {
numa.SetAffinity([]int{cpus[0]})
}
// 执行NUMA感知的任务
cpu, node := numa.GetCPUAndNode()
fmt.Printf("Worker running on CPU %d (NUMA node %d)\n", cpu, node)
}(node)
}
runtime.Gosched()
// 等待goroutine执行
time.Sleep(time.Second)
}
这个库特别适合需要优化内存访问性能的场景,特别是在多NUMA节点的服务器上运行高性能应用程序时。通过将数据和计算绑定到同一个NUMA节点,可以减少跨节点内存访问带来的性能损失。
更多关于golang NUMA感知编程工具插件库numa的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang NUMA感知编程工具插件库numa的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang NUMA感知编程工具库numa的使用
NUMA(Non-Uniform Memory Access)是一种多处理器计算机内存设计架构,在这种架构中,内存访问时间取决于内存相对于处理器的位置。在NUMA系统中,CPU访问本地内存比访问远程内存更快。
在Go语言中,可以使用numa
库来实现NUMA感知的编程。下面我将介绍如何使用这个库。
安装numa库
首先安装numa
库:
go get github.com/rakyll/numa
基本用法
1. 获取NUMA节点信息
package main
import (
"fmt"
"github.com/rakyll/numa"
)
func main() {
// 获取可用的NUMA节点数量
nodeCount := numa.NodeCount()
fmt.Printf("系统有 %d 个NUMA节点\n", nodeCount)
// 获取当前进程运行的CPU
cpu := numa.CPU()
fmt.Printf("当前运行在CPU: %d\n", cpu)
// 获取CPU所属的NUMA节点
node := numa.NodeOfCPU(cpu)
fmt.Printf("CPU %d 属于NUMA节点 %d\n", cpu, node)
}
2. 设置NUMA亲和性
package main
import (
"fmt"
"github.com/rakyll/numa"
"runtime"
)
func main() {
// 获取可用的NUMA节点
nodes := numa.Nodes()
fmt.Printf("可用NUMA节点: %v\n", nodes)
// 将当前goroutine绑定到特定NUMA节点
err := numa.RunOnNode(0, func() {
// 这里的代码会在NUMA节点0上执行
cpu := numa.CPU()
node := numa.NodeOfCPU(cpu)
fmt.Printf("在NUMA节点 %d 上执行, 当前CPU: %d\n", node, cpu)
})
if err != nil {
fmt.Printf("设置NUMA亲和性失败: %v\n", err)
}
// 设置整个进程的NUMA亲和性
err = numa.SetPreferredNode(0)
if err != nil {
fmt.Printf("设置进程NUMA亲和性失败: %v\n", err)
}
}
3. NUMA感知的内存分配
package main
import (
"fmt"
"github.com/rakyll/numa"
"unsafe"
)
func main() {
// 在特定NUMA节点上分配内存
node := 0
size := 1024 * 1024 // 1MB
ptr, err := numa.AllocOnNode(size, node)
if err != nil {
fmt.Printf("NUMA内存分配失败: %v\n", err)
return
}
defer numa.Free(ptr, size)
// 使用分配的内存
slice := (*[1 << 30]byte)(unsafe.Pointer(ptr))[:size:size]
slice[0] = 42
// 检查内存所在的NUMA节点
allocNode, err := numa.NodeOfAddr(uintptr(ptr))
if err != nil {
fmt.Printf("获取内存节点失败: %v\n", err)
} else {
fmt.Printf("分配的内存位于NUMA节点 %d\n", allocNode)
}
}
高级用法
1. NUMA感知的并行计算
package main
import (
"fmt"
"github.com/rakyll/numa"
"runtime"
"sync"
)
func worker(node int, wg *sync.WaitGroup) {
defer wg.Done()
err := numa.RunOnNode(node, func() {
// 模拟计算密集型任务
sum := 0
for i := 0; i < 100000000; i++ {
sum += i % 10
}
cpu := numa.CPU()
fmt.Printf("Worker on NUMA node %d (CPU %d) completed, sum=%d\n", node, cpu, sum)
})
if err != nil {
fmt.Printf("Worker on node %d failed: %v\n", node, err)
}
}
func main() {
nodes := numa.Nodes()
var wg sync.WaitGroup
for _, node := range nodes {
wg.Add(1)
go worker(node, &wg)
}
wg.Wait()
fmt.Println("所有工作完成")
}
2. NUMA感知的缓存对齐
package main
import (
"fmt"
"github.com/rakyll/numa"
"unsafe"
)
type Data struct {
Value1 int64
Value2 int64
}
func main() {
node := 0
// 分配NUMA对齐的内存
size := int(unsafe.Sizeof(Data{}))
ptr, err := numa.AllocOnNode(size, node)
if err != nil {
fmt.Printf("内存分配失败: %v\n", err)
return
}
defer numa.Free(ptr, size)
data := (*Data)(unsafe.Pointer(ptr))
data.Value1 = 42
data.Value2 = 24
fmt.Printf("分配的数据: Value1=%d, Value2=%d\n", data.Value1, data.Value2)
}
注意事项
-
NUMA功能需要操作系统和硬件的支持,在不支持NUMA的系统上,这些函数可能返回默认值或错误。
-
在使用
numa
库时,需要注意内存管理,确保分配的内存被正确释放。 -
过度使用NUMA绑定可能会导致负载不均衡,需要根据实际应用场景进行权衡。
-
在容器化环境中,NUMA感知可能会受到容器编排系统的影响。
通过合理使用NUMA感知编程,可以显著提高内存密集型应用程序的性能,特别是在多插槽服务器上。