numa_balancing扫描间隔调整
1. numa_next_scan2. numa_scan_period相关代码分析3. 线程迁移的周期1. numa_next_scan扫描是针对内存进行的为了保证每次只有一个线程对内存进行扫描使用mm_struct中的numa_next_scan作为控制变量。线程扫描周期的控制变量numa_scan_period要作用到numa_next_scan上来影响扫描的周期。这个过程可以参考函数task_numa_work在task_numa_work这个函数中会先比较当前时间now和numa_next_scan只有当前时间now晚于numa_next_scan才会执行后面的流程。在下一步更新numa_next_scan下一次的numa_next_scan定义为now numa_scan_period下一次的扫描时间由numa_scan_period决定存储在numa_next_scan中。| | | | --- | --- | | 1 | // cmpxchg的函数的作用它的入参是一个指向要操作的变量的指针、期望值和新值返回值是操作前的变量值。 | | 2 | // 在这里它比较numa_next_scan的当前值和migrate这个值如果相等则修改为next_scan, 否则直接return, 但是问题是numa_scan_period的取值为什么会小于1000 | | 3 | // 这里的操作可以保证对于一个mm结构体, 同一时间只有一个线程来修改 | | 4 | migrate mm-numa_next_scan; | | 5 | if (time_before(now, migrate)) | | 6 | return; | | 7 | next_scan now msecs_to_jiffies(p-numa_scan_period); | | 8 | if (cmpxchg(mm-numa_next_scan, migrate, next_scan) ! migrate) | | 9 | return; |影响task-numa_scan_period的有三个函数- task_tick_numacurr-numa_scan_period task_scan_start(curr) - update_scan_period: curr-numa_scan_period task_scan_start(curr) - update_task_scan_period: curr-numa_scan_period clamp(curr-numa_scan_period diff, task_scan_min(curr), task_scan_max(curr))task_tick_numa这个函数中的numa_scan_period只有在numa_balancing特性初次运行的时候才会生效 update_scan_period: 这个函数中的numa_scan_period要更新需要等到任务所在的node发生改变时才会产生也就是任务发生跨node的迁移时才会被调用 update_task_scan_period: 这个函数在task_numa_placement函数中被调用task_numa_placement可以认为在很频繁地被调用所以numa_scan_period很快就会增长2. numa_scan_period相关代码分析在使能numa balancing特性进行第一次扫描或者线程发生跨node迁移后会调用函数task_scan_start来设置numa_scan_period的值我们来看一下task_scan_start函数的调用流程| | | | --- | --- | | 1 | // 扫描时间由两个值中的最大值决定 max(min, period) | | 2 | // min rss / nr_scan_pages | | 3 | // period: 由min和private和share内存的比例决定 | | 4 | task_scan_start | | 5 | - task_scan_min | | 6 | // 扫描的最小时间为 rss / nr_scan_pages, 其中rss为进程总共使用的物理内存大小, nr_scan_pages为单次扫描的物理内存大小 | | 7 | - task_nr_scan_windows || | | | --- | --- | | 1 | static inline unsigned long group_faults_priv(struct numa_group *ng); | | 2 | static inline unsigned long group_faults_shared(struct numa_group *ng); | | 3 | | | 4 | // rss代表总共使用的物理内存总共使用的物理内存的大小决定了task_nr_scan_windows的大小使用的物理内存越多扫描的越快 | | 5 | static unsigned int task_nr_scan_windows(struct task_struct *p) | | 6 | { | | 7 | unsigned long rss 0; | | 8 | unsigned long nr_scan_pages; | | 9 | | | 10 | /* | | 11 | * Calculations based on RSS as non-present and empty pages are skipped | | 12 | * by the PTE scanner and NUMA hinting faults should be trapped based | | 13 | * on resident pages | | 14 | */ | | 15 | nr_scan_pages sysctl_numa_balancing_scan_size (20 - PAGE_SHIFT); | | 16 | rss get_mm_rss(p-mm); | | 17 | if (!rss) | | 18 | rss nr_scan_pages; | | 19 | | | 20 | rss round_up(rss, nr_scan_pages); | | 21 | return rss / nr_scan_pages; | | 22 | } | | 23 | | | 24 | // 由2560这个值可以推断出, 一次扫描的最短时间是100ms | | 25 | /* For sanitys sake, never scan more PTEs than MAX_SCAN_WINDOW MB/sec. */ | | 26 | #define MAX_SCAN_WINDOW 2560 | | 27 | | | 28 | static unsigned int task_scan_min(struct task_struct *p) | | 29 | { | | 30 | unsigned int scan_size READ_ONCE(sysctl_numa_balancing_scan_size); | | 31 | unsigned int scan, floor; | | 32 | unsigned int windows 1; | | 33 | | | 34 | if (scan_size MAX_SCAN_WINDOW) | | 35 | windows MAX_SCAN_WINDOW / scan_size; | | 36 | floor 1000 / windows; | | 37 | | | 38 | scan sysctl_numa_balancing_scan_period_min / task_nr_scan_windows(p); | | 39 | return max_t(unsigned int, floor, scan); | | 40 | } | | 41 | | | 42 | // 对于group shared 和private分别指的是什么 | | 43 | static unsigned int task_scan_start(struct task_struct *p) | | 44 | { | | 45 | unsigned long smin task_scan_min(p); | | 46 | unsigned long period smin; | | 47 | struct numa_group *ng; | | 48 | | | 49 | /* Scale the maximum scan period with the amount of shared memory. */ | | 50 | rcu_read_lock(); | | 51 | ng rcu_dereference(p-numa_group); | | 52 | if (ng) { | | 53 | unsigned long shared group_faults_shared(ng); | | 54 | unsigned long private group_faults_priv(ng); | | 55 | | | 56 | period * refcount_read(ng-refcount); | | 57 | period * shared 1; | | 58 | period / private shared 1; | | 59 | } | | 60 | rcu_read_unlock(); | | 61 | | | 62 | return max(smin, period); | | 63 | } | | 64 | | | 65 | static unsigned int task_scan_max(struct task_struct *p) | | 66 | { | | 67 | unsigned long smin task_scan_min(p); | | 68 | unsigned long smax; | | 69 | struct numa_group *ng; | | 70 | | | 71 | /* Watch for min being lower than max due to floor calculations */ | | 72 | smax sysctl_numa_balancing_scan_period_max / task_nr_scan_windows(p); | | 73 | | | 74 | /* Scale the maximum scan period with the amount of shared memory. */ | | 75 | ng deref_curr_numa_group(p); | | 76 | if (ng) { | | 77 | unsigned long shared group_faults_shared(ng); | | 78 | unsigned long private group_faults_priv(ng); | | 79 | unsigned long period smax; | | 80 | | | 81 | period * refcount_read(ng-refcount); | | 82 | period * shared 1; | | 83 | period / private shared 1; | | 84 | | | 85 | smax max(smax, period); | | 86 | } | | 87 | | | 88 | return max(smin, smax); | | 89 | } |numa_faults_locality[3]作为数组总共存储了3个元素第一个元素代表remote访问的pages数第二个元素代表local访问的pages数第三个元素代表迁移失败的pages数目。update_task_scan_period函数中对于numa_scan_period的更新有两种方式完全没有有效的fault访问此时任务完全处于idle状态或是所有的内存访问区域都属于不能进行numa balancing操作的的区域。或者是内存迁移失败此时要么是内存迁移太过频繁或是内存要迁去的node已经负载过重此时直接将numa_scan_period的时间设置为原来的两倍当被local/ (local remote) 70% 或是 private / (private shared) 70%此时按照比例将numa_scan_period调大最大调大的幅度为原来的30%。否则要按比例降低numa_scan_period加快扫描。| | | | --- | --- | | 1 | // 该函数在task_numa_placement函数中被调用用于更新numa_scan_period, 根据本地访存的比例, 调整扫描的周期 | | 2 | /* | | 3 | * Increase the scan period (slow down scanning) if the majority of | | 4 | * our memory is already on our local node, or if the majority of | | 5 | * the page accesses are shared with other processes. | | 6 | * Otherwise, decrease the scan period. | | 7 | */ | | 8 | static void update_task_scan_period(struct task_struct *p, | | 9 | unsigned long shared, unsigned long private) | | 10 | { | | 11 | unsigned int period_slot; | | 12 | int lr_ratio, ps_ratio; | | 13 | int diff; | | 14 | | | 15 | unsigned long remote p-numa_faults_locality[0]; | | 16 | unsigned long local p-numa_faults_locality[1]; | | 17 | | | 18 | /* | | 19 | * If there were no record hinting faults then either the task is | | 20 | * completely idle or all activity is areas that are not of interest | | 21 | * to automatic numa balancing. Related to that, if there were failed | | 22 | * migration then it implies we are migrating too quickly or the local | | 23 | * node is overloaded. In either case, scan slower | | 24 | */ | | 25 | if (local shared 0 || p-numa_faults_locality[2]) { | | 26 | p-numa_scan_period min(p-numa_scan_period_max, | | 27 | p-numa_scan_period 1); | | 28 | | | 29 | p-mm-numa_next_scan jiffies | | 30 | msecs_to_jiffies(p-numa_scan_period); | | 31 | | | 32 | return; | | 33 | } | | 34 | | | 35 | /* | | 36 | * Prepare to scale scan period relative to the current period. | | 37 | * NUMA_PERIOD_THRESHOLD scan period stays the same | | 38 | * NUMA_PERIOD_THRESHOLD scan period decreases (scan faster) | | 39 | * NUMA_PERIOD_THRESHOLD scan period increases (scan slower) | | 40 | */ | | 41 | period_slot DIV_ROUND_UP(p-numa_scan_period, NUMA_PERIOD_SLOTS); | | 42 | lr_ratio (local * NUMA_PERIOD_SLOTS) / (local remote); | | 43 | ps_ratio (private * NUMA_PERIOD_SLOTS) / (private shared); | | 44 | | | 45 | if (ps_ratio NUMA_PERIOD_THRESHOLD) { | | 46 | /* | | 47 | * Most memory accesses are local. There is no need to | | 48 | * do fast NUMA scanning, since memory is already local. | | 49 | */ | | 50 | int slot ps_ratio - NUMA_PERIOD_THRESHOLD; | | 51 | if (!slot) | | 52 | slot 1; | | 53 | diff slot * period_slot; | | 54 | } else if (lr_ratio NUMA_PERIOD_THRESHOLD) { | | 55 | /* | | 56 | * Most memory accesses are shared with other tasks. | | 57 | * There is no point in continuing fast NUMA scanning, | | 58 | * since other tasks may just move the memory elsewhere. | | 59 | */ | | 60 | int slot lr_ratio - NUMA_PERIOD_THRESHOLD; | | 61 | if (!slot) | | 62 | slot 1; | | 63 | diff slot * period_slot; | | 64 | } else { | | 65 | /* | | 66 | * Private memory faults exceed (SLOTS-THRESHOLD)/SLOTS, | | 67 | * yet they are not on the local NUMA node. Speed up | | 68 | * NUMA scanning to get the memory moved over. | | 69 | */ | | 70 | int ratio max(lr_ratio, ps_ratio); | | 71 | diff -(NUMA_PERIOD_THRESHOLD - ratio) * period_slot; | | 72 | } | | 73 | | | 74 | p-numa_scan_period clamp(p-numa_scan_period diff, | | 75 | task_scan_min(p), task_scan_max(p)); | | 76 | memset(p-numa_faults_locality, 0, sizeof(p-numa_faults_locality)); | | 77 | } |3. 线程迁移的周期线程迁移的最短时间由numa_migrate_retry决定在函数task_numa_fault函数中根据numa_migrate_retry的取值确定是否要调用task_numa_placement和函数numa_migrate_preferred来执行线程迁移。| | | | --- | --- | | 1 | /* | | 2 | * Retry to migrate task to preferred node periodically, in case it | | 3 | * previously failed, or the scheduler moved us. | | 4 | */ | | 5 | if (time_after(jiffies, p-numa_migrate_retry)) { | | 6 | task_numa_placement(p); | | 7 | numa_migrate_preferred(p); | | 8 | } |numa_migrate_retry的取值在函数中numa_migrate_preferred中被修改被定义为内存扫描周期的1/16| | | | --- | --- | | 1 | /* Periodically retry migrating the task to the preferred node */ | | 2 | interval min(interval, msecs_to_jiffies(p-numa_scan_period) / 16); | | 3 | p-numa_migrate_retry jiffies interval; |

相关新闻

32*32音频处理器常见误区:独立AEC通道数量影响会议效果

32*32音频处理器常见误区:独立AEC通道数量影响会议效果

32*32音频处理器选型指南:解析独立AEC通道与算力架构的关键作用在构建中大型会议室、多功能厅或指挥中心的音视频系统时,32*32音频处理器通常作为核心信号处理节点存在。然而,许多系统集成商和用户在设备筛选过程中,容易陷入“通道…

2026/10/16 17:33:31 阅读更多 →
C++ string优化技术:SBO与COW原理与性能对比

C++ string优化技术:SBO与COW原理与性能对比

1. 揭开C string的神秘面纱在C开发者的日常工作中,string可能是最熟悉却又最陌生的伙伴。作为标准库中最常用的容器之一,它的内部实现机制直接影响着程序性能和内存使用效率。今天我们就来深入探讨现代C string实现中的两大核心技术:SBO&…

2026/10/16 17:33:35 阅读更多 →
AS3.0项目集成Starling框架:GPU硬件加速优化2D渲染性能实战

AS3.0项目集成Starling框架:GPU硬件加速优化2D渲染性能实战

在Flash/ActionScript 3.0项目后期,尤其是涉及复杂动画、粒子效果或大型游戏时,你是否遇到过性能瓶颈?CPU占用率飙升,帧率却持续走低,复杂的显示列表让渲染线程不堪重负。这正是传统Flash Player基于CPU的矢量渲染在面…

2026/10/9 7:19:52 阅读更多 →

最新新闻

AI应用架构师:解决大模型落地“最后一公里”的关键角色

AI应用架构师:解决大模型落地“最后一公里”的关键角色

1. 先聊一个扎心的事实:AI项目为什么总在"最后一公里"翻车这几年我在不少企业里见过同一个剧本:算法团队花三个月训出一个效果很不错的模型,准确率坐实验室里测出来95%往上,团队士气高涨,老板也发了朋友圈预…

2026/10/16 17:31:46 阅读更多 →
多 Agent 协同熵增:为什么三个 AI 助手反而更慢更错

多 Agent 协同熵增:为什么三个 AI 助手反而更慢更错

1. 多 Agent 不是“人多力量大”,而是“开会开到散伙”——一个被严重低估的协同熵增问题你有没有试过让三个 AI 助手同时帮你写一份技术方案?一个查资料,一个写初稿,一个润色——结果文档里出现了三套术语体系、两套时间线、四次…

2026/10/16 17:31:46 阅读更多 →
SVM多分类实战:一对多、一对一与ECOC策略对比及调参指南

SVM多分类实战:一对多、一对一与ECOC策略对比及调参指南

1. SVM多分类的整体设计思路做机器学习分类任务的朋友都清楚,SVM(支持向量机)在很多场景下依然是一把好手,尤其样本量不大、特征维度比较高的时候,它的表现往往比深度学习还要稳。但有个绕不开的门槛——SVM天生是用来…

2026/10/16 17:31:46 阅读更多 →
NARX与BP神经网络实战对比:动态时序预测谁更强?

NARX与BP神经网络实战对比:动态时序预测谁更强?

做预测分析这行当久了,你会发现一个很有意思的现象:一聊到时序预测,大多数人的第一反应要么是统计模型,要么直接冲深度学习,反倒把神经网络家族里那对“看起来不那么新”的选手——NARX延时动态神经网络和BP神经网络—…

2026/10/16 17:31:46 阅读更多 →
SDL安全开发生命周期落地指南:从威胁建模到DevSecOps实践

SDL安全开发生命周期落地指南:从威胁建模到DevSecOps实践

1. SDL到底在解决什么问题:从一次线上事故说起有段时间,我所在的团队上线了一个面向外部用户的文件上传功能。功能本身不复杂,前后端联调也算顺利,产品验收也通过了。然而上线两周后的某天凌晨,监控平台突然报警——某…

2026/10/16 17:31:46 阅读更多 →
深度学习环境配置终极指南:PyTorch+CUDA+cuDNN版本匹配实战

深度学习环境配置终极指南:PyTorch+CUDA+cuDNN版本匹配实战

搞了这么多年深度学习,我发现自己花在“配置环境”上的时间,一点不比调模型的时间少。PyTorch CUDA cuDNN这套组合,几乎是每个深度学习开发者的必经之路,也是最容易让人劝退的一步。网上教程一搜一大把,但版本匹配问…

2026/10/16 17:29:44 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →