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)
	}
}

主要功能说明

  1. numa.CPUCount() - 获取系统CPU核心数量
  2. numa.GetCPUAndNode() - 获取当前goroutine运行的CPU和NUMA节点
  3. numa.NodeCount() - 获取NUMA节点数量
  4. numa.NodeToCPUs(node int) - 获取指定NUMA节点上的CPU列表
  5. 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

1 回复

更多关于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)
}

注意事项

  1. NUMA功能需要操作系统和硬件的支持,在不支持NUMA的系统上,这些函数可能返回默认值或错误。

  2. 在使用numa库时,需要注意内存管理,确保分配的内存被正确释放。

  3. 过度使用NUMA绑定可能会导致负载不均衡,需要根据实际应用场景进行权衡。

  4. 在容器化环境中,NUMA感知可能会受到容器编排系统的影响。

通过合理使用NUMA感知编程,可以显著提高内存密集型应用程序的性能,特别是在多插槽服务器上。

回到顶部