如何使Julia父进程及所有子进程遵循taskset设置的CPU亲和性
强制把Julia主进程+所有子进程绑到指定CPU核心
嘿,这个问题我太熟了——taskset确实只会给你直接启动的主进程设亲和性,但Julia用addprocs之类的方式启动子进程时,经常会主动重置CPU掩码,导致子进程跑去其他核心。下面给你几个彻底解决的办法:
方法1:用cgroups(最靠谱,内核级强制限制)
Linux的cgroups是专门做资源隔离的,能把整个进程树死死按在指定核心上,完全不受程序自身设置影响:
操作步骤(以主流的cgroups v2为例):
- 先创建一个专门的cgroup(需要root权限):
sudo mkdir -p /sys/fs/cgroup/cpuset/julia_bound - 设置允许用的CPU核心(这里是1和2):
sudo echo 1,2 > /sys/fs/cgroup/cpuset/julia_bound/cpuset.cpus - 配置内存节点(如果你的机器不是NUMA架构,直接写0就行):
sudo echo 0 > /sys/fs/cgroup/cpuset/julia_bound/cpuset.mems - 把当前shell加入这个cgroup,这样之后启动的所有进程都会继承限制:
echo $$ | sudo tee /sys/fs/cgroup/cpuset/julia_bound/cgroup.procs - 现在启动Julia就行,主进程和所有子进程都会被锁在CPU1-2上:
julia Foo.jl - 用完之后退出当前shell,或者手动删掉cgroup清理:
sudo rmdir /sys/fs/cgroup/cpuset/julia_bound
方法2:在Julia代码里强制子进程继承亲和性
如果你不想折腾系统级设置,可以在Foo.jl开头加一段代码,让Julia启动子进程时自动继承主进程的CPU掩码:
# 先获取主进程的CPU亲和性掩码 using Libdl function get_current_cpu_affinity() # 生成足够存下所有CPU的掩码数组 mask_size = div(Sys.CPU_THREADS, 64) + 1 mask = zeros(UInt64, mask_size) # 调用系统函数获取当前进程的亲和性 ccall((:sched_getaffinity, "libc"), Cint, (Cpid_t, Csize_t, Ptr{UInt64}), 0, sizeof(mask), mask) # 解析掩码得到可用的CPU核心编号 cpu_list = Int[] for (i, chunk) in enumerate(mask) for bit in 0:63 if (chunk >> bit) & 1 == 1 push!(cpu_list, (i-1)*64 + bit) end end end return cpu_list end # 启动子进程时强制设置相同的CPU亲和性 cpu_ids = get_current_cpu_affinity() addprocs(; exeflags=`--cpu-affinity $(join(cpu_ids, ","))`)
这样不管Julia怎么启动子进程,都会自动绑定到和主进程一样的核心上。
方法3:用numactl(适合NUMA架构服务器)
如果你的机器是NUMA架构(比如多CPU服务器),numactl比taskset更靠谱,能同时绑定CPU和内存,而且子进程会自动继承设置:
# 指定物理核心1和2 numactl --physcpubind=1,2 julia Foo.jl
要是你想绑定NUMA节点而不是单个核心,就用--cpunodebind参数。
为啥单独用taskset不行?
简单说,Julia的工作进程在启动时,会主动调用sched_setaffinity来重置CPU亲和性(可能是为了默认均衡负载),这就把taskset给主进程设的掩码覆盖了。而cgroups和numactl是从内核层面强制限制,程序自己改不了,所以能彻底解决问题。
内容的提问来源于stack exchange,提问作者gertian
相关产品推荐
相关产品推荐

