Go Metrics SDK Tag 校验性能优化实践
背景
Metrics SDK 是与字节内场时序数据库 ByteTSD 配套的用户指标打点 SDK,在字节内数十万服务中集成,应用广泛,因此 SDK 的性能优化是个重要和持续性的话题。本文主要以 Go Metrics SDK 为例,讲述对打点 API 的 hot-path 优化的实践。
用户在使用 SDK API 进行打点时,需要传入指标对应的 Tag:
tags := []m.T{{Name: "foo", Value: "a"}, {Name: "bar", Value: "b"}}
metric.WithTags(tags...).Emit(m.Incr(1))
SDK 内部需要对用户传入的 Tag Value 的合法性进行校验,IsValidTagValue,是 SDK 中对 Tag Value 进行字符合法性校验的 util 函数,在对内部一些用户的业务使用 pprof 拉取 profile 时,发现这两个函数的 CPU 消耗占整个打点 API 过程的10%~20%,由于该函数发生在打点 API 的 hot-path 上,因此有必要对其进行进一步优化。

分析
当前实现
我们先看一下 IsValidTagValue 函数内部的实现方式,是否有可优化的点。当前的实现,对于通过 API 传入的每一个Tag Value,会进行以下操作来判断其合法性:
先判断是否是在 Letter、Number 的范围内,是则直接通过;
存储所有允许的特殊字符白名单,遍历 Tag Value 对比其每个字符是否在白名单内。
var (// these runes are valid in tag valueswhiteListRunes = []rune{'_', '-', '.', '%', ':', ' ', '[', ']', ',', '%','/', ':', ';', '<', '=', '>', '@', '~'}
)func IsValidTagValue(s string) bool {if len(s) == 0 || len(s) > maxTagLen {return false}for i, r := range s {if r < minValidChar || r > maxValidChar {return false}if unicode.IsLetter(r) || unicode.IsNumber(r) || isRuneInWhiteList(r) {continue}return false}return true
}
该实现的时间复杂度简单分析如下:
对于由 Letter、Number 这样的合法字符构成的字符串(大部分场景),其时间复杂度是:
对于全由特殊字符构成的字符串,其时间复杂度是:
整个字符串的时间复杂度将介于 到之间
问题点
可以看到,从当前实现看,一个主要影响性能的点是白名单列表的循环遍历对比操作,我们需要考虑可能的优化方式来降低这个操作的时间复杂度。
优化
优化一:使用 Lookup Table,空间换时间
Metrics SDK 所有允许的合法的字符,实际上是 ASCII 的一个子集,也就是说其所有可能的字符最多只有128个,因此,我们可以通过空间换时间的方式,将对白名单的 O(n) 遍历操作转换为 O(1) 的查表操作:
提前对这128个字符建立一个包含128个成员的数组,在每一个 offset 上标记对应字符是否合法(合法则标记为
1
),这样就建立了一个快速的 lookup table对于要校验的每一个字符,只要将其转化为数组 offset,直接取数组成员值判断是否为
1
即可

table := [128]uint8{...}
// fill flags
for i := 0; i < 128; i++ {if unicode.IsNumber(rune(i)) || unicode.IsLetter(rune(i)) || isRuneInWhiteList(rune(i)) {table[i] = 1}
}str := "hello"for _, char := range []byte(str) {if r > maxValidChar {return false}if table[char] != 1 {return false}
}
return true
Benchmark
goos: linux
goarch: amd64
pkg: code.byted.org/gopkg/metrics_core/utils
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkLookupAlgoValid
BenchmarkLookupAlgoValid/baseline
BenchmarkLookupAlgoValid/baseline-8 2839345 478.9 ns/op
BenchmarkLookupAlgoValid/lookup-arraytable
BenchmarkLookupAlgoValid/lookup-arraytable-8 6673456 167.8 ns/op
可以看到,速度提升60%
优化二:使用 SIMD,提升并行度
基于 Lookup Table 的校验方式,将字符串校验的时间复杂度稳定在了, 但有没有可能进一步减少对字符串每一个字符的遍历次数,比如一次校验16个字符?
我们知道,SIMD 指令是循环展开优化的常用思路,那么这里是否可以引入 SIMD 来进一步提升运算并行度和效率?
答案是肯定的,以 intel x86 架构为例,参考其 Intrinsics Guide,在不同的 SIMD 指令集上提供了多个可以实现在不同大小的 lookup table 中查找数据的指令,这些指令可以作为我们加速方案的基础:

注:可以通过
cat /proc/cpuinfo
命令来查看机器支持的simd指令集
鉴于 vpermi2b 指令的支持目前不是很普遍的原因,我们考虑使用 pshufb 来实现一个 SIMD 版本,但我们的Lookup Table 需要调整下,因为:
虽然我们基于 bitmap 实现的 Lookup Table 是 128 bits,刚好可以填充 128 bits 的寄存器
但 pshufb 是按字节进行 lookup 的,128 bits 的寄存器支持16字节的 lookup
因此,我们需要将 bitmap lookup table 做一次升维,变成一个16*8 bits 的二维 lookup table,做两次递进的行、列 lookup 完成查找,基于该思路,可以实现一次校验16个字符,大大提升并行度。
整体方案
该方案主要参考这篇文章:SIMDized check which bytes are in a set(http://0x80.pl/articles/simd-byte-lookup.html)
构建 bitmap table
对于一个 ASCII 字符,我们用其低 4bits 作为 lookup table 的 row index,用高 3bits 作为 lookup table 的 column index,这样对128个 ASCII 字符建立如下的一个二维 bitmap table:

Lookup 流程
我们先实现一个纯 go 语言版本的基于二维 bitmap lookup table 的方案,以便于理解其中的关键逻辑:
table := [16]uint8{}
// fill flags
for i := 0; i < 128; i++ {if unicode.IsNumber(rune(i)) || unicode.IsLetter(rune(i)) || isRuneInWhiteList(rune(i)) {lowerNibble := i & 0x0fupperNibble := i >> 4table[lowerNibble] |= 1 << upperNibble}
}str := "hello"for _, char := range []byte(str) {if r > maxValidChar {return false}lowerNibble := uint8(r) & 0x0fupperNibble := uint8(r) >> 4if table[lowerNibble]&(1<<upperNibble) == 0 {return false}
}
return true
如上代码示例,可以看到,判断某个字符合法的关键逻辑是:
通过 table[lowerNibble] 获取table第 lowerNibble 行内容,然后再看其第 upperNibble 个 bit 位是否为0
而 SIMD 版本,即是将上述的每一步操作都使用对应的 SIMD 指令变成对16个字节的并行操作,SIMD 的关键操作流程以及和上述 go 代码的对应关系如下:

代码实现
在 go 语言中,想要使用 SIMD,需要写 plan9 汇编,而编写 plan9 通常有两种方式:
手撕,可借助 avo 这样的工具
C code 转 plan9,可借助 goat、c2goasm 这样的工具
这里采用 C code 转 plan9 的方式,先写一个 C 版本:
注:由于 goat 工具限制,不能很好的支持 C 代码中的常量定义,因此以下示例通过函数参数定义用到的 sm、hm 常量
#include <tmmintrin.h>// is_valid_string returns 1 if all chars is in table, returns 0 else.
void is_valid_string(char* table, char* strptr, long strlen, char* sm, char* hm, char* rt) {__m128i bitmap = _mm_loadu_si128((__m128i*)table);__m128i shift_mask = _mm_loadu_si128((__m128i*)sm);__m128i high_mask = _mm_loadu_si128((__m128i*)hm);size_t n = strlen/16;for (size_t i = 0; i < n; i++){__m128i input = _mm_loadu_si128((__m128i*)strptr);__m128i rows = _mm_shuffle_epi8(bitmap, input);__m128i hi_nibbles = _mm_and_si128(_mm_srli_epi16(input, 4), high_mask);__m128i cols = _mm_shuffle_epi8(shift_mask, hi_nibbles);__m128i tmp = _mm_and_si128(rows, cols);__m128i result = _mm_cmpeq_epi8(tmp, cols);size_t mask = _mm_movemask_epi8(result);if (mask != 65535) {*rt = 0;return;}strptr = strptr + 16;}size_t left = strlen%16;for (size_t i = 0; i < left; i++){size_t lower = strptr[i] & 0x0f;size_t higher = strptr[i] >> 4;if ((table[lower] & (1<<higher)) == 0) {*rt = 0;return;}}*rt = 1;return;
}
通过以下命令转为 plan9:
goat is_valid_string.c -03 -mssse3
生成的 plan9 代码如下:
//go:build !noasm && amd64
// AUTO-GENERATED BY GOAT -- DO NOT EDITTEXT ·_is_valid_string(SB), $0-48MOVQ table+0(FP), DIMOVQ strptr+8(FP), SIMOVQ strlen+16(FP), DXMOVQ sm+24(FP), CXMOVQ hm+32(FP), R8MOVQ rt+40(FP), R9WORD $0x8949; BYTE $0xd2 // movq %rdx, %r10LONG $0x3ffac149 // sarq $63, %r10LONG $0x3ceac149 // shrq $60, %r10WORD $0x0149; BYTE $0xd2 // addq %rdx, %r10LONG $0x0f428d48 // leaq 15(%rdx), %raxLONG $0x1ff88348 // cmpq $31, %raxJB LBB0_4LONG $0x076f0ff3 // movdqu (%rdi), %xmm0LONG $0x096f0ff3 // movdqu (%rcx), %xmm1LONG $0x6f0f41f3; BYTE $0x10 // movdqu (%r8), %xmm2WORD $0x894d; BYTE $0xd0 // movq %r10, %r8LONG $0x04f8c149 // sarq $4, %r8WORD $0xc031 // xorl %eax, %eaxLBB0_2:LONG $0x1e6f0ff3 // movdqu (%rsi), %xmm3LONG $0xe06f0f66 // movdqa %xmm0, %xmm4LONG $0x00380f66; BYTE $0xe3 // pshufb %xmm3, %xmm4LONG $0xd3710f66; BYTE $0x04 // psrlw $4, %xmm3LONG $0xdadb0f66 // pand %xmm2, %xmm3LONG $0xe96f0f66 // movdqa %xmm1, %xmm5LONG $0x00380f66; BYTE $0xeb // pshufb %xmm3, %xmm5LONG $0xe5db0f66 // pand %xmm5, %xmm4LONG $0xe5740f66 // pcmpeqb %xmm5, %xmm4LONG $0xccd70f66 // pmovmskb %xmm4, %ecxLONG $0xfffff981; WORD $0x0000 // cmpl $65535, %ecxJNE LBB0_8LONG $0x10c68348 // addq $16, %rsiLONG $0x01c08348 // addq $1, %raxWORD $0x394c; BYTE $0xc0 // cmpq %r8, %raxJB LBB0_2LBB0_4:LONG $0xf0e28349 // andq $-16, %r10WORD $0xb041; BYTE $0x01 // movb $1, %r8bWORD $0x294c; BYTE $0xd2 // subq %r10, %rdxJE LBB0_9WORD $0xc031 // xorl %eax, %eaxLBB0_7:LONG $0x1cbe0f4c; BYTE $0x06 // movsbq (%rsi,%rax), %r11WORD $0x8945; BYTE $0xda // movl %r11d, %r10dLONG $0x0fe28341 // andl $15, %r10dLONG $0x04ebc141 // shrl $4, %r11dLONG $0x0cbe0f42; BYTE $0x17 // movsbl (%rdi,%r10), %ecxLONG $0xd9a30f44 // btl %r11d, %ecxJAE LBB0_8LONG $0x01c08348 // addq $1, %raxWORD $0x3948; BYTE $0xd0 // cmpq %rdx, %raxJB LBB0_7LBB0_9:WORD $0x8845; BYTE $0x01 // movb %r8b, (%r9)BYTE $0xc3 // retqLBB0_8:WORD $0x3145; BYTE $0xc0 // xorl %r8d, %r8dWORD $0x8845; BYTE $0x01 // movb %r8b, (%r9)BYTE $0xc3 // retq
对应的 Go Wrapper 代码如下:
var (// these runes are valid in tag valueswhiteListRunes = []rune{'_', '-', '.', '%', ':', ' ', '[', ']', ',', '%','/', ':', ';', '<', '=', '>', '@', '~'}rcBitTable [16]uint8smTable [16]int8hmTable [16]uint8
)//go:noescape
func _is_valid_string(table unsafe.Pointer, str unsafe.Pointer, len int32, sm, hm unsafe.Pointer, rt unsafe.Pointer)func init() {// build tablesfor i := 0; i < 128; i++ {if unicode.IsNumber(rune(i)) || unicode.IsLetter(rune(i)) || isRuneInWhiteList(rune(i)) {lowerNibble := i & 0x0fupperNibble := i >> 4rcBitTable[lowerNibble] |= 1 << upperNibble}}smTable = [16]int8{1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128}hmTable = [16]uint8{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}
}func IsValidTagValueLookup2dBitTableSIMD(s string) bool {l := len(s)if l == 0 || len(s) > maxTagLen {return false}sptr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)var rt byte_is_valid_string(unsafe.Pointer(&rcBitTable), sptr, int32(len(s)), unsafe.Pointer(&smTable), unsafe.Pointer(&hmTable), unsafe.Pointer(&rt))return rt != 0
}
Benchmark
先做一个通用的 benchmark,待校验的 string 长度从1 ~ 20不等:
goos: linux
goarch: amd64
pkg: code.byted.org/gopkg/metrics_core/utils
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkLookupAlgoValid
BenchmarkLookupAlgoValid/baseline
BenchmarkLookupAlgoValid/baseline-8 2574217 510.5 ns/op
BenchmarkLookupAlgoValid/lookup-arraytable
BenchmarkLookupAlgoValid/lookup-arraytable-8 6347204 193.7 ns/op
BenchmarkLookupAlgoValid/lookup-2d-bittable-simd
BenchmarkLookupAlgoValid/lookup-2d-bittable-simd-8 6133671 185.2 ns/op
可以看到,SIMD 版本在平均水平上与 arraytable 相当
由于 SIMD 优势主要体现在长字符串时,因此,我们使用一组长度为20左右的 string,再次 benchmark:
goos: linux
goarch: amd64
pkg: code.byted.org/gopkg/metrics_core/utils
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkLookupAlgoValidLong
BenchmarkLookupAlgoValidLong/baseline
BenchmarkLookupAlgoValidLong/baseline-8 3523198 356.4 ns/op
BenchmarkLookupAlgoValidLong/lookup-arraytable
BenchmarkLookupAlgoValidLong/lookup-arraytable-8 8434142 153.3 ns/op
BenchmarkLookupAlgoValidLong/lookup-2d-bittable-simd
BenchmarkLookupAlgoValidLong/lookup-2d-bittable-simd-8 13621970 87.29 ns/op
可以看到,在长 string 上 SIMD 版本表现出非常大的优势,相对于 arraytable 版本再次提升50%
结论
通过 lookup table + SIMD 的方式优化,字符校验的整体性能可以提升2~4倍
但由于在 Go 中 plan9 汇编无法内联,因此在待校验的字符串较短时不能体现其优势
Reference
https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#
http://0x80.pl/articles/simd-byte-lookup.html
https://fullyfaithful.eu/simd-byte-scan/
https://gorse.io/posts/avx512-in-golang.html#convert-assembly
http://0x80.pl/notesen/2016-04-03-avx512-base64.html
本文作者郭刚平,来自字节跳动 Dev Infra - APM - 观测数据引擎团队,我们提供日均数十PB级可观测性数据采集、存储和查询分析的引擎底座,致力于为业务、业务中台、基础架构建设完整统一的可观测性技术支撑能力。
相关文章:

Go Metrics SDK Tag 校验性能优化实践
背景 Metrics SDK 是与字节内场时序数据库 ByteTSD 配套的用户指标打点 SDK,在字节内数十万服务中集成,应用广泛,因此 SDK 的性能优化是个重要和持续性的话题。本文主要以 Go Metrics SDK 为例,讲述对打点 API 的 hot-path 优化的…...

二叉树问题——前/中/后/层遍历问题(递归与栈)
摘要 博文主要介绍二叉树的前/中/后/层遍历(递归与栈)方法 一、前/中/后/层遍历问题 144. 二叉树的前序遍历 145. 二叉树的后序遍历 94. 二叉树的中序遍历 102. 二叉树的层序遍历 103. 二叉树的锯齿形层序遍历 二、二叉树遍历递归解析 // 前序遍历递归LC144_二叉树的前…...

Vue3问题:如何实现级联菜单的数据懒加载?
前端功能问题系列文章,点击上方合集↑ 序言 大家好,我是大澈! 本文约3100字,整篇阅读大约需要5分钟。 本文主要内容分三部分,第一部分是需求分析,第二部分是实现步骤,第三部分是问题详解。 …...

STM32-电源管理(实现低功耗)
电源管理 STM32 HAL库对电源管理提供了完善的函数和命令。 工作模式(高功耗->低功耗):运行、睡眠、停止、待机。 若备份域电源正常供电,备份域内的RTC都可以正常运行,备份域内的寄存器的数据会被保存,不…...

vue 自己捣鼓周日程日历组件
需求:想要一个周日程表,记录每天的计划,点击可查看详情。可自定义时间段通过后台获取时间段显示 分析: 通过需求,超级课程表app这款软件其中课表和这个需求很像,只不过这个需求第一列的时间段是自定义的,不是上午下午两个,但是原理都差不多 原本想找一些第三方插件使…...

【力扣】2127. (分类讨论 + 拓扑排序)参加会议的最多员工数
【力扣】2127. (分类讨论 拓扑排序)参加会议的最多员工数 文章目录 【力扣】2127. (分类讨论 拓扑排序)参加会议的最多员工数1. 题目介绍2. 思路(**分类讨论 拓扑排序**)3. 解题代码4. Danger参考 1. 题…...
Flutter——最详细(Map)使用教程
Map简介 键值对的集合,您可以使用其关联的键从中检索值。 普通的 HashMap是无序的(不保证顺序),LinkedHashMap 按键插入顺序迭代,而像 SplayTreeMap 这样的排序映射按排序顺序迭代键。 1,添加元素 addEntri…...
vue的入门第一课
Vue.js是一款流行的JavaScript框架,用于构建交互式Web应用程序。本文将详细介绍Vue.js的基础知识,包括Vue.js的历史、设计模式、构造函数参数、el、data、computed、method、watch以及差值的使用。 Vue.js是什么? Vue.js是一款用于构建用户…...
已解决:conda找不到对应版本的cudnn如何解决?
1.解决方法 配置深度学习环境时,打算安装cudatoolkit11.2和cudnn8.1,当使用conda install cudnn8.0时,却搜索不到这个版本的包,解决方法如下: conda search cudnn -c conda-forge然后就可以使用如下命令进行安装对应…...
大语言模型的学习路线和开源模型的学习材料《二》
第三层 LLMs to Artifact 第一重 langchain 【LLMs 入门实战 —— 十二 】基于 本地知识库 的高效 🤖langchain-ChatGLM 介绍:langchain-ChatGLM是一个基于本地知识的问答机器人,使用者可以自由配置本地知识,用户问题的答案也是基于本地知识生成的。【LLMs 入门实战 ——…...

Flask-SQLAlchemy事件钩子介绍
一、前言 前几天在搜资料的时候无意中看到有介绍SQLAlchemy触发器,当时感觉挺奇怪的,触发器不是数据库层面的概念吗,怎么flask-SQLAlchemy这个ORM框架会有这玩意。 二、SQLAlchemy触发器一个简单例子 考虑到效率博客表中有两个字段…...

C++——list
目录 list介绍 list的函数接口 构造函数 push_front和pop_front push_back和pop_back insert erase 迭代器 front和back size resize empty clear list::sort unique reverse 迭代器的实现 list介绍 list是一种可以在常数范围内在任意位置进行插入和删除的序列…...

【Linux】第九站:make和makefile
文章目录 一、 Linux项目自动化构建工具make/Makefile1.make/makefile工作现象2.依赖关系与依赖方法3.如何清理4.为什么这里我们需要带上clean5.连续的make6.特殊符号 二、Linux下实现一个简单的进度条1.回车换行2.缓冲区3.倒计时的实现 一、 Linux项目自动化构建工具make/Make…...
一文了解什么是WebSocket
WebSocket 允许我们创建“实时”应用程序,与传统 API 协议相比,该应用程序速度更快且开销更少。 一、WebSocket 是如何工作的 按照传统的定义,WebSocket是一种双工协议,主要用于客户端-服务器通信通道。它本质上是双向的&…...
redis是什么
redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。和Memcached类似。redis支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。 一、 基本…...

基于深度学习的人脸专注度检测计算系统 - opencv python cnn 计算机竞赛
文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习的人脸专注度…...

跨境电商的新引擎:崛起的网红经济
随着全球数字化时代的崛起,跨境电商成为了国际贸易的新引擎,而在这个巨大的变革浪潮中,网红经济正在崭露头角,成为这一引擎的有力推动者。在这篇文章中,我们将深入探讨网红经济如何催生跨境电商的新动力,以…...
P2006 赵神牛的游戏 python解法
赵神牛的游戏 题目描述 在 DNF 中,赵神牛有一个缔造者,他一共有 k k k 点法力值,一共有 m m m 个技能,每个技能耗费的法力值为 a i a_i ai,可以造成的伤害为 b i b_i bi,而 boss 的体力值为 n n…...

Unity的碰撞检测(六)
温馨提示:本文基于前一篇“Unity的碰撞检测(五)”继续探讨两个游戏对象具备刚体的BodyType均为Dynamic,但是Collision Detection属性不同的碰撞检测,阅读本文则默认已阅读前文。 (一)测试说明 在基于两个游戏对象都具…...

从前序与中序遍历序列构造二叉树
代码如下,开袋即食 class Solution {private Map<Integer,Integer> map;public TreeNode buildTree(int[] preorder, int[] inorder) {map new HashMap<>();for(int i 0;i<preorder.length;i){map.put(inorder[i],i);}return build(preorder,inord…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...