惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
H
Hacker News: Front Page
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
N
Netflix TechBlog - Medium
U
Unit 42
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
Google Online Security Blog
Google Online Security Blog
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
T
Tenable Blog
博客园 - 叶小钗
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
量子位
P
Proofpoint News Feed
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
宝玉的分享
宝玉的分享
Recorded Future
Recorded Future
The Register - Security
The Register - Security
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
S
Schneier on Security
V
Vulnerabilities – Threatpost
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
G
GRAHAM CLULEY
G
Google Developers Blog
月光博客
月光博客
V
V2EX
T
Troy Hunt's Blog
A
Arctic Wolf

See you soon

哟,好久不见,无线打印 | See you soon 试试将文章版本化管理吧 | See you soon 使用 Quadlet 将 Podman 中的 Postgres 当作 systemd 服务运行 | See you soon 大他者,那个无时无刻都在盯着你的东西 | See you soon Laws of Software Engineering,软件工程定律 | See you soon 浅记多因素身份认证 | See you soon Linux 内核中的度量单位 | See you soon 重置 GPG 智能密钥 | See you soon 向 NAS 引入 samba | See you soon 无法重复键入的 Fcitx5 | See you soon ZFS 降级事故 | See you soon 记被 XanMod Kernel 和 AppArmor 联合坑的一次踩坑 | See you soon agent 的 skill 与 toolcall | See you soon 记一次服务器被挂恶意挖矿二进制 | See you soon 活着的 Arc | See you soon 令 acme.sh 使用 Cloudflare 的 DNS API 签发与续签证书 | See you soon 如我所见,梦破碎的时候 | See you soon 74LS 家族手册 | See you soon JDK Projects 备忘录 | See you soon 关于历史 | See you soon 用 curl 下载 OnePlus 的 ROM | See you soon 实用命令切片 | See you soon 再见,Oh My Zsh。 | See you soon 你不应该复用 strings.Builder | See you soon 博客的明日 | See you soon 被 AppArmor 击杀的 Dockge | See you soon AI 时代的自我 | See you soon 支持删除的布隆过滤器 | See you soon 基于栈的虚拟机与基于寄存器的虚拟机 | See you soon RVA23 包含了什么 | See you soon Rust 的边界检查是否已经有很大的进步 | See you soon 使用 Rust 编写操作系统:Barebones | See you soon 使用 Rust 编写操作系统:引言 | See you soon 编译器笔记:rope | See you soon 编译器笔记:CST | See you soon Paradoxical 札记 | See you soon 简单的相似去重算法(基于向量) | See you soon 更加现代的 PaperMC Minecraft 插件设计指南 | See you soon 简单的 CFG 语法分析方法 | See you soon 简单地使用 Caddy 实现 CORS 配置 | See you soon 让 OpenCV 可以被静态链接 | See you soon 图片搜索笔笺 | See you soon 电路板设计笔记-保护 | See you soon 使用 Rust 实现 SnowflakeId | See you soon 农夫乐事 FaQ 启示 | See you soon 使用 Rust 实现拓展系统札记 | See you soon 遗传学定律的代码实现 | See you soon EN:The Journey of Rust and Procps | See you soon ZH:Rust 与 Procps 之旅 | See you soon 使用 debootstrap 与 schroot 构建一个纯净环境 | See you soon GPG添加新的用户信息 | See you soon GSoC2024 笔记:使用 Rust 重新实现 procps | See you soon JWT 小册 | See you soon 使用密钥登录 SSH | See you soon Guava:Cache | See you soon 编译器笔记:增量编译 | See you soon Gradle秘境:添加一个类似modCompileOnly的依赖块 | See you soon Minecraft:原始经济系统设计简述 | See you soon TinyRemapper笔记 | See you soon MicroOS:进阶 | See you soon MicroOS:起步 | See you soon Swapfile 指北 | See you soon GPG 物理密钥的安装与密钥的迁移 | See you soon 用GPG签名告诉大家这就是我的提交 | See you soon DFS:深度优先搜索 | See you soon 文章翻译:从init.vim到init.lua | See you soon 目录遍历攻击 | See you soon 如何挂载.img文件,以及如何使用QEMU模拟arm64环境 | See you soon 在树莓派上编译 OpenCV | See you soon 关于卸载BlueStacks后遇到的问题 | See you soon
于 Tokio 中卸载 CPU Bound 任务 | See you soon
Krysztal Huang · 2026-02-22 · via See you soon

记一次后端计算密集性能优化。

在 Rust 中使用 async/await 可以有效提升 IO 密集程序的吞吐量,因为大部分工作是可以挂起等待的。但是在一套系统中难免会有一些任务无法被挂起等待,这种任务通常是需要持续运算的计算密集型任务,也叫作 CPU-bound task。

因为我使用了 argon2id 来对密码进行哈希和验证,而 argon2id 是出了名的工作缓慢,是个很典型的计算密集型任务,混合在 IO 密集程序中会严重拖慢 IO 密集程序的工作速度,因此我们需要将其转移到 IO 任务池之外进行运行。


对一个计算密集的任务进行优化应尽可能从对代码更改影响较小的地方开始修改。

我们有如下代码:

// MORE...

pub fn verify(&self, password: impl Into<String>) -> Result<bool, Error> {

let password_hash = PasswordHash::new(&self.0)?;

match DEFAULT_ARGON_CFG

.clone()

.verify_password(password.into().as_bytes(), &password_hash)

{

Ok(()) => Ok(true),

Err(e) => match e {

argon2::password_hash::Error::Password => Err(e.into()),

_ => Ok(false),

},

}

}

// MORE...

可以看到这个代码是使用了 argon2 对密码进行校验,是个非常典型的计算密集任务。

负载分离

我们可以将这部分代码转移到其他线程进行计算,能帮助我们完成这一点的工具是 tokio::task::spawn_blocking

In general, issuing a blocking call or performing a lot of compute in a future without yielding is problematic, as it may prevent the executor from driving other futures forward. This function runs the provided closure on a thread dedicated to blocking operations. See the CPU-bound tasks and blocking code section for more information.

Tokio will spawn more blocking threads when they are requested through this function until the upper limit configured on the Builder is reached. After reaching the upper limit, the tasks are put in a queue. The thread limit is very large by default, because spawn_blocking is often used for various kinds of IO operations that cannot be performed asynchronously. When you run CPU-bound code using spawn_blocking, you should keep this large upper limit in mind. When running many CPU-bound computations, a semaphore or some other synchronization primitive should be used to limit the number of computation executed in parallel.

既然现在知道了改造的初步逻辑,那么我们继续执行我们的计划吧。

// MORE

pub async fn verify(&self, password: impl Into<String>) -> Result<bool, Error> {

let password = password.into();

let phc = self.0.clone();

tokio::task::spawn_blocking(move || {

let password_hash = PasswordHash::new(&phc)?;

match DEFAULT_ARGON_CFG.verify_password(password.as_bytes(), &password_hash) {

Ok(()) => Ok(true),

Err(argon2::password_hash::Error::Password) => {

Err(Error::Password(argon2::password_hash::Error::Password))

}

Err(_) => Ok(false),

}

})

.await?

}

// MORE

可以看到修改其实非常简单。但是要注意了,送入 tokio::task::spawn_blocking 的任务是会一起运行的,因此我们需要对其进行一些限制避免压力太大搞爆服务器。

使用信号量

根据 tokio 的官方文档以及我们的常识,我们可以使用信号量 Semaphore 来达到这一点:

// MORE

static MAX_CPU_BOUND_SEMAPHORE: LazyLock<Semaphore> = LazyLock::new(|| Semaphore::new(num_cpus::get() * 2));

pub async fn verify(&self, password: impl Into<String>) -> Result<bool, Error> {

let password = password.into();

let phc = self.0.clone();

tokio::task::spawn_blocking(move || async move {

// NOTE: The consts::MAX_CPU_BOUND_SEMAPHORE will not be closed forever

let _ = MAX_CPU_BOUND_SEMAPHORE.acquire().await;

let password_hash = PasswordHash::new(&phc)?;

match DEFAULT_ARGON_CFG.verify_password(password.as_bytes(), &password_hash) {

Ok(()) => Ok(true),

Err(argon2::password_hash::Error::Password) => {

Err(Error::Password(argon2::password_hash::Error::Password))

}

Err(_) => Ok(false),

}

})

.await?

.await

}

// MORE