Linux static_key原理与应用

news2024/11/15 8:16:26

文章目录

  • 背景
  • 1. static-key的使用方法
    • 1.1. static-key定义
    • 1.2 初始化
    • 1.3 条件判断
    • 1.4 修改判断条件
  • 2、示例代码
    • 参考链接

背景

内核中有很多判断条件在正常情况下的结果都是固定的,除非极其罕见的场景才会改变,通常单个的这种判断的代价很低可以忽略,但是如果这种判断数量巨大且被频繁执行,那就会带来性能损失了。内核的static-key机制就是为了优化这种场景,其优化的结果是:对于大多数情况,对应的判断被优化为一个NOP指令,在非常有场景的时候就变成jump XXX一类的指令,使得对应的代码段得到执行。
在这里插入图片描述

1. static-key的使用方法

1.1. static-key定义

static_key 结构体的定义如下:

#ifdef CONFIG_JUMP_LABEL

struct static_key {
	atomic_t enabled;
/*
 * Note:
 *   To make anonymous unions work with old compilers, the static
 *   initialization of them requires brackets. This creates a dependency
 *   on the order of the struct with the initializers. If any fields
 *   are added, STATIC_KEY_INIT_TRUE and STATIC_KEY_INIT_FALSE may need
 *   to be modified.
 *
 * bit 0 => 1 if key is initially true
 *	    0 if initially false
 * bit 1 => 1 if points to struct static_key_mod
 *	    0 if points to struct jump_entry
 */
	union {
		unsigned long type;
		struct jump_entry *entries;
		struct static_key_mod *next;
	};
};

#else
struct static_key {
	atomic_t enabled;
};
#endif	/* CONFIG_JUMP_LABEL */

如果没有定义CONFIG_JUMP_LABEL,则static_key 退化成atomic变量。

1.2 初始化

#define DEFINE_STATIC_KEY_TRUE(name)	\
	struct static_key_true name = STATIC_KEY_TRUE_INIT
#define DEFINE_STATIC_KEY_FALSE(name)	\
	struct static_key_false name = STATIC_KEY_FALSE_INIT
#define STATIC_KEY_TRUE_INIT  (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE,  }
#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, }

#define STATIC_KEY_INIT_TRUE                    \
    { .enabled = { 1 },                    \
      .entries = (void *)JUMP_TYPE_TRUE }
#define STATIC_KEY_INIT_FALSE                    \
    { .enabled = { 0 },                    \
      .entries = (void *)JUMP_TYPE_FALSE }

false和true的主要区别就是enabled 是否为1.

1.3 条件判断

#ifdef CONFIG_JUMP_LABEL

/*
 * Combine the right initial value (type) with the right branch order
 * to generate the desired result.
 *
 *
 * type\branch|	likely (1)	      |	unlikely (0)
 * -----------+-----------------------+------------------
 *            |                       |
 *  true (1)  |	   ...		      |	   ...
 *            |    NOP		      |	   JMP L
 *            |    <br-stmts>	      |	1: ...
 *            |	L: ...		      |
 *            |			      |
 *            |			      |	L: <br-stmts>
 *            |			      |	   jmp 1b
 *            |                       |
 * -----------+-----------------------+------------------
 *            |                       |
 *  false (0) |	   ...		      |	   ...
 *            |    JMP L	      |	   NOP
 *            |    <br-stmts>	      |	1: ...
 *            |	L: ...		      |
 *            |			      |
 *            |			      |	L: <br-stmts>
 *            |			      |	   jmp 1b
 *            |                       |
 * -----------+-----------------------+------------------
 *
 * The initial value is encoded in the LSB of static_key::entries,
 * type: 0 = false, 1 = true.
 *
 * The branch type is encoded in the LSB of jump_entry::key,
 * branch: 0 = unlikely, 1 = likely.
 *
 * This gives the following logic table:
 *
 *	enabled	type	branch	  instuction
 * -----------------------------+-----------
 *	0	0	0	| NOP
 *	0	0	1	| JMP
 *	0	1	0	| NOP
 *	0	1	1	| JMP
 *
 *	1	0	0	| JMP
 *	1	0	1	| NOP
 *	1	1	0	| JMP
 *	1	1	1	| NOP
 *
 * Which gives the following functions:
 *
 *   dynamic: instruction = enabled ^ branch
 *   static:  instruction = type ^ branch
 *
 * See jump_label_type() / jump_label_init_type().
 */

#define static_branch_likely(x)							\
({										\
	bool branch;								\
	if (__builtin_types_compatible_p(typeof(*x), struct static_key_true))	\
		branch = !arch_static_branch(&(x)->key, true);			\
	else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
		branch = !arch_static_branch_jump(&(x)->key, true);		\
	else									\
		branch = ____wrong_branch_error();				\
	likely(branch);								\
})

#define static_branch_unlikely(x)						\
({										\
	bool branch;								\
	if (__builtin_types_compatible_p(typeof(*x), struct static_key_true))	\
		branch = arch_static_branch_jump(&(x)->key, false);		\
	else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
		branch = arch_static_branch(&(x)->key, false);			\
	else									\
		branch = ____wrong_branch_error();				\
	unlikely(branch);							\
})

#else /* !CONFIG_JUMP_LABEL */

#define static_branch_likely(x)		likely(static_key_enabled(&(x)->key))
#define static_branch_unlikely(x)	unlikely(static_key_enabled(&(x)->key))

#endif /* CONFIG_JUMP_LABEL */

可见同样依赖HAVE_JUMP_LABEL。如果没有定义的话,直接退化成likely和unlikely
static_branch_unlikelystatic_branch_likely 只是填充指令的方式不同(可以参考上面的代码注释), 当static_key为false时,都会进入else逻辑语句中。

if (static_branch_unlikely((&static_key)))
    do likely work;
else
    do unlikely work

1.4 修改判断条件

使用static_branch_enablestatic_branch_disable可以改变static_key 状态

#define static_branch_enable(x)        static_key_enable(&(x)->key)
#define static_branch_disable(x)    static_key_disable(&(x)->key)

底层是调用static_key_slow_dec, static_key_slow_dec来改变key->enabled计数。

static inline void static_key_enable(struct static_key *key)
{
    int count = static_key_count(key);

    WARN_ON_ONCE(count < 0 || count > 1);
    
    if (!count)
        static_key_slow_inc(key);
}
static inline void static_key_disable(struct static_key *key)
{
    int count = static_key_count(key);

    WARN_ON_ONCE(count < 0 || count > 1);
    
    if (count)
        static_key_slow_dec(key);
}
static inline void static_key_slow_inc(struct static_key *key)
{
	STATIC_KEY_CHECK_USE(key);
	atomic_inc(&key->enabled);
}

static inline void static_key_slow_dec(struct static_key *key)
{
	STATIC_KEY_CHECK_USE(key);
	atomic_dec(&key->enabled);
}

2、示例代码

下面我们用一段代码来分析static-key对程序分支跳转硬编码的影响。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/static_key.h>

DEFINE_STATIC_KEY_FALSE(key);

void func(int a){
    if (static_branch_unlikely(&key)) {  
        printk("my_module: Feature is enabled\n");
    } else {
        printk("my_module: Feature is disabled\n");
    }
}

static int __init my_module_init(void) {
    pr_info("my_module: Module loaded\n");
    int a = 1;
    func(a);
    static_branch_enable(&key);
    func(a);
    return 0;
}

static void __exit my_module_exit(void) {
    pr_info("my_module: Module unloaded\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Kernel Module with Static Key");

func汇编代码如下:

0000000000000000 <func>:
   0:   a9bf7bfd        stp     x29, x30, [sp, #-16]!
   4:   910003fd        mov     x29, sp
   8:   d503201f        nop
   c:   90000000        adrp    x0, 0 <func>
  10:   91000000        add     x0, x0, #0x0
  14:   94000000        bl      0 <printk>
  18:   a8c17bfd        ldp     x29, x30, [sp], #16
  1c:   d65f03c0        ret
  20:   90000000        adrp    x0, 0 <func>
  24:   91000000        add     x0, x0, #0x0
  28:   94000000        bl      0 <printk>
  2c:   17fffffb        b       18 <func+0x18>

func中不适用static-key时,汇编代码如下:

void func(int a){
    if (a) {  
        printk("my_module: Feature is enabled\n");
    } else {
        printk("my_module: Feature is disabled\n");
    }
}

0000000000000000 <func>:
   0:   a9bf7bfd        stp     x29, x30, [sp, #-16]!
   4:   910003fd        mov     x29, sp
   8:   340000a0        cbz     w0, 1c <func+0x1c>
   c:   90000000        adrp    x0, 0 <func>
  10:   91000000        add     x0, x0, #0x0
  14:   94000000        bl      0 <printk>
  18:   14000004        b       28 <func+0x28>
  1c:   90000000        adrp    x0, 0 <func>
  20:   91000000        add     x0, x0, #0x0
  24:   94000000        bl      0 <printk>
  28:   a8c17bfd        ldp     x29, x30, [sp], #16
  2c:   d65f03c0        ret

对比可以发现,在0x8地址处,使用static-key编码在编译时将cbz指令替换为了nop指令,减少了程序运行时对比次数。

参考链接

  1. Linux内核中的static-key机制
  2. Linux内核jump label与static key的原理与示例
  3. static-keys.html | 静态键
  4. Linux Jump Label/static-key机制详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1013243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

18 自增长主键的实现 以及 记录的插入

前言 这里主要是 探索一下 mysql 的自增长主键 和 insert into tz_test (field1) values ("12111111111"); 的实现 这里 有一些地方 会有不求甚解的地方, 不然 篇幅 就有点太长了 测试表结构 mysql 主键自增长 读取自增长值的地方, 读取 table->autoinc 作…

激活函数总结(三十四):激活函数补充(FReLU、CReLU)

激活函数总结&#xff08;三十四&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 FReLU激活函数2.2 CReLU激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、ELU、SELU、GELU、Softmax、Sof…

React 全栈体系(七)

第四章 React ajax 一、理解 1. 前置说明 React本身只关注于界面, 并不包含发送ajax请求的代码前端应用需要通过ajax请求与后台进行交互(json数据)react应用中需要集成第三方ajax库(或自己封装) 2. 常用的ajax请求库 jQuery: 比较重, 如果需要另外引入不建议使用axios: 轻…

AI无法提振台积电股价

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;台积电的股价已经从最高点下跌了18%&#xff0c;很多期权交易员正在押注台积电的股价会进一步下跌。 &#xff08;2&#xff09;华尔街分析师目前也下调了台积电的收入和盈利预期…

EasyUI combobox 实现搜索(模糊匹配)功能

很简单的一个下拉框搜索模糊匹配功能&#xff0c;在此记录&#xff1a; 1&#xff1a;页面实现&#xff1a; <select class"easyui-combobox" name"combobox" id"combobox" style"width:135px;height:25px;" headerValue"请选…

9.19-21,openEuler与您相约2023欧洲开源峰会

2023年9月19日-21日&#xff0c;openEuler将参加在西班牙毕尔巴鄂举办的 OSSUMMIT 2023&#xff08;Open Source Summit Europe 2023&#xff09;&#xff0c;这是openEuler继去年正式亮相后的第二次全面参加该峰会。 Open Source Summit Europe是由Linux基金会主办&#xff0…

来喽!!炒鸡详细的“数据在内存中的存储”真的来喽!

目录​​​​​​​ 1. 整数在内存中的存储 1.1 ⼆进制介绍 1.1.1 2进制转10进制 1.1.2 10进制转2进制 1.1.3 2进制转8进制 1.1.4 2进制转16进制 1.2 原码、反码、补码 2. ⼤⼩端字节序和字节序判断 2.1 什么是⼤⼩端&#xff1f; 2.2 为什么有⼤⼩端? 2.3 练习 …

01目标检测-问题引入

目录 一、目标检测问题定义 二、目标检测过程中的常见的问题 三、目标检测VS图像分类区别 目标检测&#xff1a; 图像分类&#xff1a; 总结&#xff1a; 四、目标检测VS目标分割 目标分割&#xff1a; 目标检测是计算机视觉领域的一个重要任务&#xff0c;旨在从图像或…

[管理与领导-93]:IT基层管理者 - 扩展技能 - 5 - 职场丛林法则 -7- 复杂问题分析能力与复杂问题的解决能力:系统化思维

目录 前言&#xff1a; 一、系统化思维 VS 分解思维 1.1 系统化思维 1.2 分解思维 二、中医与西医思维模式的区别 三、正向闭环/正反馈 VS 负向闭环/负反馈 VS 开环 3.1 开环与管理 3.2 闭环与管理 3.3 生态系统是闭环系统 3.4 团队是一个闭环系统 3.5 正向闭环/正反…

有趣的设计模式——适配器模式让两脚插头也能使用三孔插板

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 场景与问题 众所周知&#xff0c;我们国家的生活用电的电压是220V而笔记本电脑、手机等电子设备的工作压没有这么高。为了使笔记本、手机等设备可以使用220V的生活用电就需…

API(九)基于协程的并发编程SDK

一 基于协程的并发编程SDK 场景&#xff1a; 收到一个请求会并发发起多个请求,使用openresty提供的协程说明&#xff1a; 这个是高级课程,如果不理解可以先跳过遗留&#xff1a; APSIX和Kong深入理解openresty 标准lua的协程 ① 早期提供的轻量级协程SDK ngx.thread ngx…

国内外交通数据集介绍(附参数说明)

国外数据集 NGSIM数据集 NGSIM数据集采集自美国&#xff0c;数据集中包含两条高速公路&#xff08;US-101&#xff0c;I-80&#xff09;及两条城市道路&#xff08;lankershim&#xff0c;peachtree&#xff09;的数据&#xff0c;每条道路的采集时间为45min。数据集中包含包含…

荣耀亲选耳机Wingcloud X5s Pro新品上市:9月15日首次亮相

荣耀亲选耳机Wingcloud X5s Pro以其46dB自适应主动降噪、Hi-Res高保真音质和40小时超长续航的特点而备受期待。首次亮相的新品外观圆润流畅&#xff0c;充满了现代感和时尚气息&#xff0c;无论是冰岛白的典雅大方&#xff0c;还是钛银色的成熟经典&#xff0c;每一个细节显示着…

记录crack某IDE插件过程

声明&#xff1a;本文仅记录学习过程&#xff0c;已对关键位置脱敏处理&#xff0c;未提供任何工具&#xff0c;请支持正版。 反编译jar包 使用cfr进行对插件核心jar包MyBxxxxxx-obfuss.jar进行反编译&#xff0c;在本地生成a.txt。 java -jar cfr-0.152.jar MyBxxxx-obfuss.…

车云一体化数字孪生再跨步!数据「精准反哺」整车定制化开发

汽车智能化竞争焦点&#xff0c;已经从单车智能延伸至云端。 尤其是在软件定义汽车的驱动下&#xff0c;汽车软件开发贯穿整个汽车生命周期。而基于SOA的车云一体化软件开发能力&#xff0c;通过跨车云的服务化架构、数据闭环等&#xff0c;支撑着传统V型瀑布式开发&#xff0…

苹果的市场份额下滑了,事实证明国内能与苹果竞争的只有它

分析机构给出二季度国内600美元以上手机市场的报告&#xff0c;报告指出苹果的市场份额已显著下滑&#xff0c;国产手机品牌占有的市场份额则已取得较大的提升&#xff0c;让人意外的是成功抢走苹果市场份额的仍然是之前的那家国产手机品牌。 据分析机构的数据显示&#xff0c;…

Zabbix监控组件及流程

Zabbix 由5大组件构成 Zabbix Web、Zabbix Server、Zabbix Proxy、Zabbix Database、Zabbix Agent Zabbix监控系统具体监控系统流程如图&#xff1a; Zabbix Web Zabbix Web是基于PHP语言编写的WEB UI界面&#xff0c;展示Zabbix整个监控平台监控数据、配置信息、方便对整个…

医学图像分割:Self-supervised Semantic Segmentation: Consistency over Transformation

论文作者&#xff1a;Sanaz Karimijafarbigloo,Reza Azad,Amirhossein Kazerouni,Yury Velichko,Ulas Bagci,Dorit Merhof 作者单位&#xff1a;University of Regensburg;RWTH Aachen University;Iran University of Science and Technology;Northwestern University;Fraunho…

unity UDP 通信

客户端 接收端 &#xff1a; using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; using UnityEngine.UI;public cla…

轻松剪辑大量视频,批量AI智剪让你省时又省力

在今天的数字时代&#xff0c;视频已经成为了信息传播的主要方式之一。无论是个人用户还是专业人士&#xff0c;都需要对视频进行剪辑以满足各种需求。但是&#xff0c;对于一些需要处理大量视频的情况&#xff0c;传统的剪辑方式往往费时费力。幸运的是&#xff0c;有一种名为…