当前位置: 首页 > news >正文

差生文具多之(二): perf

栈回溯和符号解析是使用 perf 的两大阻力,本文以应用程序 fio 的观测为例子,提供一些处理它们的经验法则,希望帮助大家无痛使用 perf。

前言

系统级性能优化通常包括两个阶段:性能剖析和代码优化:

  1. 性能剖析的目标是寻找性能瓶颈,查找引发性能问题的原因及热点代码;

  2. 代码优化的目标是针对具体性能问题而优化代码或调整编译选项,以改善软件性能。

在步骤一性能剖析阶段,最常用的工具就是 perf。perf 是 linux 官方提供的性能分析工具,被包含在 Linux 内核源码树中。它是一个庞大的工具集合,功能相当繁杂。但在工作中,通常我们只会使用到 perf 其中相当小的一个子集,主要包含以下四个步骤:

  1. perf record: 采集数据,采的时间越长越心安;

  2. perf report: 查看采集数据,因为采集太长时间,解析数据会卡很久,我们试图理解数据,通常无法理解;

  3. perf script: 尝试查看原始采样点,通常无法理解;

  4. 生成火焰图: 色彩丰富,通常发给领导理解。

综上,后三个步骤是我们无法控制的,本文主要聊聊如何在步骤一尽量生成可信的采样数据。

workflow of perf

虽然听起来调侃,但上述步骤确实是标准的分析流程,毕竟有火焰图发明人 Brandon 的背书:

@Brandon

可以看到,它们被包含在 perf 工作流第三列的 capture stacks 中,简单回顾一下这四个步骤:

  1. perf record: 通过指定 -g 选项可以收集系统整体的函数调用栈(包含用户态和内核态),默认以 4000HZ 的频率收集,大约每秒生成 4000 个采样点,被保存在 perf.data 文件中;

$ perf record -g -C 0 -- sleep 1
[ perf record: Captured and wrote 0.906 MB perf.data (4001 samples) ]
  1. perf report: 通过解析 perf.data,生成热点函数占用 CPU 的比例。例如以下输出中,CPU0 大部分时间(99.73%)停留在内核代码的 idle 函数中,即 CPU0 大部分时间处于空闲状态:

$ perf report --no-child --stdio
99.73%  swapper          [kernel.kallsyms]  [k] native_safe_halt|---native_safe_haltacpi_idle_do_entryacpi_idle_entercpuidle_enter_statecpuidle_enterdo_idlecpu_startup_entrystart_kernelsecondary_startup_64_no_verify
  1. perf script: 查看每个采样样本(栈),例如以下栈样本表明: cpu-clock:pppH: 事件于时间 45399.463561 发生,在 CPU0 触发了中断,中断打断的任务是进程号为 0 的内核线程 swapper,栈从下往上看,被打断时 CPU 正在执行 native_safe_halt 偏移 0xe 处的指令:

$ perf script
swapper     0 [000] 45399。463561:     250000 cpu-clock:pppH: ffffffffa234c45e native_safe_halt+0xe ([kernel.kallsyms])ffffffffa234c806 acpi_idle_do_entry+0x46 ([kernel.kallsyms])ffffffffa1f4bafb acpi_idle_enter+0x9b ([kernel.kallsyms])ffffffffa211efb7 cpuidle_enter_state+0x87 ([kernel.kallsyms])ffffffffa211f33c cpuidle_enter+0x2c ([kernel.kallsyms])ffffffffa1b16ff4 do_idle+0x234 ([kernel.kallsyms])ffffffffa1b171ef cpu_startup_entry+0x6f ([kernel.kallsyms])ffffffffa3601262 start_kernel+0x518 ([kernel.kallsyms])ffffffffa1a00107 secondary_startup_64_no_verify+0xc2 ([kernel.kallsyms])
  1. 使用脚本生成火焰图,以下是官网例图:

可以发现,后续的分析步骤都基于步骤一采集得到的 perf.data。显然,只有获取到足够精准的调用栈信息,后续才能准确定位到性能瓶颈。可惜的是,获取函数调用栈并没有一个通用解,导致我们需要额外了解一些小知识。

choose your unwinder

获取函数调用栈过程又称栈回溯(unwind),栈回溯的方法被称为 unwinder,常见的 unwinder 有:

  1. fp:perf 默认选项,ARM 和 X86 都支持,消耗低;

  2. dwarf:通过 --call-graph=dwarf 指定,ARM 和 X86 都支持,对CPU和磁盘消耗高;

  3. lbr:通过 --call-graph=lbr 指定,仅 Intel 新型号支持,消耗低,但可回溯的栈深度有限;

  4. orc:内核 unwinder,无需指定。 在 perf record 中,若不通过 --call-graph 指定 unwinder,默认使用 fp 作为用户态栈的 unwinder;至于内核态的 unwinder,不由 perf 参数指定,由内核编译选项控制,低版本内核使用 fp,高版本内核使用 orc。

因此问题转化为:用户态使用哪个 unwinder 是更合适的?结论先行,以下是可供参考的方案:

  1. Intel CPU:优先使用 lbr,lbr 的好处是硬件实现,精准可靠,大部分情况下深度够用;

  2. ARM 架构:优先使用 fp,因为 ARM 架构寄存器比较多,保留了寄存器记录栈基址;

  3. X86 上没有 lbr 时:优先使用 dwarf,虽然 X86 架构也把栈基址保存在 %rbp,但只要编译优化大于等于 -O1 ,%rbp 寄存器基本作为通用寄存器使用,使得在 X86 上用 fp 获取用户态栈大部分时候不可靠。有以下注意点:
    1. 在 linux 5.19 版本以下,dwarf 可能采样不到动态链接库的栈(参考提交 perf unwind: Fix egbase for ld.lld linked objects);

    2. dwarf 需要复制保存每一个采样点的用户栈,因此采样期间 CPU 消耗较高,生成的采样数据也远大于其它 unwinder;

    3. 如果 dwarf 无法满足需求,可以 gcc 编译时添加选项 -fno-omit-frame-pointer 放弃复用 %rbp 寄存器的编译优化,重新编译应用后使用 fp。虽然该选项无法百分百保证 %rbp 一定可靠,但总体可信。

让我们通过在 X86 架构上观测应用程序 fio,对这些 unwinder 有个初步的了解:

$ perf record -a --user-callchains --call-graph=dwarf -p `pidof fio` -o perf.data.dwarf -- sleep 2
$ perf report --no-ch --stdio -i perf.data.dwarf10.69%  fio      [kernel.kallsyms]  [k] iowrite16|---syscallio_submit0x55a0a986682e # <- 我们会在下下节解决符号问题td_io_committd_io_queue0x55a0a985945a <-0x55a0a985b7d0 <- start_thread__GI___clone (inlined)
$ perf record -a --user-callchains --call-graph=fp -p `pidof fio` -o perf.data.fp -- sleep 2
$ perf report --no-ch --stdio -i perf.data.fp8.27%  fio      [kernel.kallsyms]  [k] iowrite16|---syscall|--0.75%--0x70700000707|--0.75%--0x62d0000062d|--0.75%--0x5e1000005e1|--0.75%--0x55b0000055a|--0.75%--0x54800000548|--0.75%--0x52f0000052f|--0.75%--0x51000000510|--0.75%--0x44f0000044f|--0.75%--0x3cb000003cb|--0.75%--0x39800000398--0.75%--0x37c0000037b

以上采集数据的命令中使用 --user-callchains 选项指定了 perf 采样时只采集用户栈,排除掉我们暂时不关心的内核栈。输出中可以看到虽然 dwarf 采集得到的栈没有被完全翻译,但正确地回溯到了进程刚诞生的函数 __GI___clone,这表明 dwarf 采样得到了完整的栈;反观 fp,只得到了些奇怪的地址。我们的方案三是有效的!

what do dwarf do

为叙述完整,该节补充一点 dwarf 栈回溯原理,不影响 perf 使用,不涉及的朋友可以跳转下一节解决符号问题。

在编译过程中 gcc 无论是否指定 -g 选项, 默认都会生成 .eh_frame 和 .eh_frame_hdr 段. gcc 在翻译代码为汇编代码时, 会帮忙插上一些 CFI 伪指令, 如

$ gcc -S test.c           # c语言生成汇编代码
$ vim test.s              # 查看汇编代码
$ cat test.s.cfi_startproc # 刚进函数, 当前我们处于 callee 栈帧的起始处, 更新 CFA = rsp + 8pushq %rbp# 每次 push 寄存器到栈上, 需要将 CFA += 8, 因为相比上一状态需要多往前走一个单位才是 caller 的栈帧.cfi_def_cfa_offset 16.cfi_offset 6, -16 # 并且更新该寄存器关于 CFA 的偏移, 使回溯过程可以恢复该寄存器的值# ...movq %rsp %rbp # 将 rsp 寄存器赋值给 rbp.cfi_def_cfa_register 6 # 将寄存器 6 (rbp) 定义为 CFA 寄存器, 之后 CFA 的计算都基于 rbp# ...leave.cfi_def_cfa 7, 8 # leave 中将 rbp 寄存器的值赋值给 rsp, 即 rsp 此时指向 callee 栈帧开始处, 此时 CFA = rsp + 8.cfi_endproc
$ readelf -wF test.o # 查看对应的 .eh_frame 印证
0000000000000661 rsp+8    u     c-8   
0000000000000662 rsp+16   c-16  c-8   
0000000000000665 rbp+16   c-16  c-8   
00000000000006a6 rsp+8    c-16  c-8 

其中 CFA (Canonical Frame Address, which is the address of %rsp in the caller frame) 指上一级调用者的堆栈指针.

如上所示, 汇编器会将这些 CFI 伪指令收集到可执行文件中的 .eh_frame 段. 典型形式如下:

$ readelf -wF a.out 
Contents of the .eh_frame section:00000000 0000000000000014 00000000 CIE "zR" cf=1 df=-8 ra=16LOC           CFA      ra    
0000000000000000 rsp+8    u     ...000000c8 0000000000000044 0000009c FDE cie=00000030 pc=00000000000006b0..0000000000000715LOC           CFA      rbx   rbp   r12   r13   r14   r15   ra    
00000000000006b0 rsp+8    u     u     u     u     u     u     c-8   
00000000000006b2 rsp+16   u     u     u     u     u     c-16  c-8   
00000000000006b4 rsp+24   u     u     u     u     c-24  c-16  c-8   
00000000000006b9 rsp+32   u     u     u     c-32  c-24  c-16  c-8   
00000000000006bb rsp+40   u     u     c-40  c-32  c-24  c-16  c-8   
00000000000006c3 rsp+48   u     c-48  c-40  c-32  c-24  c-16  c-8   
00000000000006cb rsp+56   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
00000000000006d8 rsp+64   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
000000000000070a rsp+56   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
000000000000070b rsp+48   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
000000000000070c rsp+40   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
000000000000070e rsp+32   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
0000000000000710 rsp+24   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
0000000000000712 rsp+16   c-56  c-48  c-40  c-32  c-24  c-16  c-8   
0000000000000714 rsp+8    c-56  c-48  c-40  c-32  c-24  c-16  c-8  

可以看到 .eh_frame 总体架构由 CIE 和 FDE 组成。通常一个 CIE 代表一个文件, 一个 FDE 代表一个函数. 其中核心的是 FDE 的组织:

利用 .eh_frame 进行栈 unwind 时候, 遵循以下步骤:

  1. 根据当前的PC在.eh_frame中找到对应的条目,根据条目提供的各种偏移计算其他信息。

  2. 首先根据CFA = rsp+4,把当前rsp+4得到CFA的值。再根据CFA的值计算出通用寄存器和返回地址在堆栈中的位置。

  3. 通用寄存器栈位置计算。例如:rbx = CFA-56。

  4. 返回地址ra的栈位置计算。ra = CFA-8。

  5. 根据ra的值,重复步骤1到4,就形成了完整的栈回溯。

handle missing symbols

函数调用栈本质是一串地址,perf 会尽量将地址翻译人类可读的符号。在以下样本点中,可以看到 IP 寄存器保存的地址属于 libc 库,它被正确翻译为 syscall+0x1d,但再往下回溯,我们只知道 syscall 函数是由 libaio 库某不知名函数调用的。这里出现 [unknown] 通常由于可执行程序的符号被裁剪所致,裁剪符号是有效减小可执行程序体积的做法。

$ perf script -D -i perf.data.dwarf
259594741631398 0x2d840 [0x20f8]: PERF_RECORD_SAMPLE(IP, 0x1): 273245/273258: 0xffffffff89d1869d period: 250000 addr: 0
... FP chain: nr:0
[...]
.... IP    0x00007f3afb87f52d
... ustack: size 8192, offset 0xe0
[...]
fio 273258 259594.741631:     250000 cpu-clock:pppH: 7f3afb87f52d syscall+0x1d (/usr/lib64/libc-2.28.so)7f3afc50ab7d [unknown] (/usr/lib64/libaio.so.1.0.1)55a0a9866a95 [unknown] (/usr/bin/fio)55a0a98197a5 td_io_getevents+0x75 (/usr/bin/fio)55a0a983b216 io_u_queued_complete+0x66 (/usr/bin/fio)55a0a98577d4 [unknown] (/usr/bin/fio)55a0a98591fa [unknown] (/usr/bin/fio)55a0a985b7d0 [unknown] (/usr/bin/fio)7f3afc0db179 start_thread+0xe9 (/usr/lib64/libpthread-2.28.so)7f3afb884dc2 __GI___clone+0x42 

那怎么将符号补全呢?我们可以通过安装 -debuginfo-dbgsym 包解决,例如对于 fio:

# centos 上,先使能 yum 的 debuginfo 源,再安装对应应用的 -debuginfo 包即可
$ cat /etc/yum.repos.d/CentOS-Linux-Debuginfo.repo 
[debuginfo]
name=CentOS Linux $releasever - Debuginfo
baseurl=http://debuginfo.centos.org/$releasever/$basearch/
gpgcheck=0
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
$ yum clean all && yum makecache
$ yum -y install fio-debuginfo.x86_64# ubuntu 上,先导入调试符号签名密钥,再安装对应应用的 -dbgsym 包即可
$ apt install ubuntu-dbgsym-keyring
$ apt install fio-dbgsym

补全后的栈如下所示:

$ perf script -i perf.data.dwarf
fio  2469  2823.211391:     250000 cpu-clock:pppH: 7f03631a89bd syscall+0x1d (/usr/lib64/libc-2.28.so)7f0363ef1c14 io_submit+0x34 (/usr/lib64/libaio.so.1.0.1)555976f418ce fio_libaio_commit+0xde (/usr/bin/fio)555976ef4a98 td_io_commit+0x58 (/usr/bin/fio)555976ef4fb5 td_io_queue+0x3f5 (/usr/bin/fio)555976f344ea do_io+0x71a (/usr/bin/fio)555976f36880 thread_main+0x18b0 (/usr/bin/fio)555976f38561 run_threads+0xcb1 (/usr/bin/fio)

后记

当你面对一个性能问题,如果选择使用 perf 观测,那么问题就变成了三个,另外两个是在解决性能问题前,必须先解决栈回溯和符号解析,前者影响观测准确性,后者影响观测可读性。perf 大部分时候都帮忙做好了,但如果遇到了些小困难,希望本文能有幸帮上一点忙。

相关文章:

差生文具多之(二): perf

栈回溯和符号解析是使用 perf 的两大阻力&#xff0c;本文以应用程序 fio 的观测为例子&#xff0c;提供一些处理它们的经验法则&#xff0c;希望帮助大家无痛使用 perf。 前言 系统级性能优化通常包括两个阶段&#xff1a;性能剖析和代码优化&#xff1a; 性能剖析的目标是寻…...

【SPI和API有什么区别】

✅什么是SPI&#xff0c;和API有什么区别 ✅典型解析&#x1f7e2;拓展知识仓&#x1f7e2;如何定义一个SPI&#x1f7e2;SPI的实现原理 ✅SPI的应用场景SpringDubbo ✅典型解析 Java 中区分 API和 SPI&#xff0c;通俗的进: API和 SPI 都是相对的概念&#xff0c;他们的差别只…...

Day67力扣打卡

打卡记录 美丽塔 II&#xff08;前缀和 单调栈&#xff09; 链接 class Solution:def maximumSumOfHeights(self, maxHeights: List[int]) -> int:n len(maxHeights)stack collections.deque()pre, suf [0] * n, [0] * nfor i in range(n):while stack and maxHeights…...

什么是网站监控?

网站监控是跟踪网站的可用性和性能&#xff0c;以最小化宕机时间&#xff0c;优化性能并确保顺畅的用户体验。维护网站正常运行对于任何企业来说都是至关重要的&#xff0c;因而对大多数业务来说&#xff0c;网站应用监控都是一个严峻的挑战。Applications Manager网站应用监控…...

游戏软件提示d3dcompiler_43.dll的五个解决方法,亲测靠谱

在使用电脑进行工作&#xff0c;玩游戏的时候&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“D3DCompiler_43.dll丢失”的提示。D3DCompiler_43.dll是一个非常重要的动态链接库文件。它是由DirectX SDK提供的&#xff0c;用于编译和优化DirectX着色器代码的…...

python使用opencv提取视频中的每一帧、最后一帧,并存储成图片

提取视频每一帧存储图片 最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&…...

说说对React refs 的理解?应用场景?

先了解&#xff0c;是什么&#xff1f; React 中的 Refs提供了一种方式&#xff0c;允许我们访问 DOM节点或在 render方法中创建的 React元素。 本质为ReactDOM.render()返回的组件实例&#xff0c;如果是渲染组件则返回的是组件实例&#xff0c;如果渲染dom则返回的是具体的do…...

Pytorch 读取t7文件

Pytorch 1.0以上可以使用&#xff1a; import torchfileth_path r"./path/xx.t7" data torchfile.load(th_path)print(data.shape)若data的尺寸为0&#xff0c;则将torch版本降为0.4.1&#xff0c;并使用以下函数&#xff1a; from torch.utils.serialization im…...

【YOLOV8预测篇】使用Ultralytics YOLO进行检测、分割、姿态估计和分类实践

目录 一 安装Ultralytics 二 使用预训练的YOLOv8n检测模型 三 使用预训练的YOLOv8n-seg分割模型 四 使用预训练的YOLOv8n-pose姿态模型 五 使用预训练的YOLOv8n-cls分类模型 <...

[Linux] MySQL数据库之索引

一、索引的相关知识 1.1 索引的简介 索引是一个排序列表&#xff0c;包含索引值和包含该值的数据行的物理地址&#xff08;类似于 c 语言链表&#xff0c;通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索…...

【期末考试】计算机网络、网络及其计算 考试重点

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 计算机网络及其计算 期末考点 &#x1f680;数…...

力扣labuladong——一刷day79

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣785. 判断二分图二、力扣886. 可能的二分法 前言 给你一幅「图」&#xff0c;请你用两种颜色将图中的所有顶点着色&#xff0c;且使得任意一条边的两个…...

【数据结构入门精讲 | 第十篇】考研408排序算法专项练习(二)

在上文中我们进行了排序算法的判断题、选择题的专项练习&#xff0c;在这一篇中我们将进行排序算法中编程题的练习。 目录 编程题R7-1 字符串的冒泡排序R7-1 抢红包R7-1 PAT排名汇总R7-2 统计工龄R7-1 插入排序还是堆排序R7-2 龙龙送外卖R7-3 家谱处理 编程题 R7-1 字符串的冒…...

【ES实战】Elasticsearch6开始的CCR

【ES实战】学习使用Elasticsearch6开始的CCR 本文涉及官网文章地址 OverviewRequirements for leader indicesAutomatically following indicesGetting started with cross-cluster replicationUpgrading clusters CCR > Cross-cluster replication 文章目录 【ES实战】学…...

Deployment Pay

axure watermark...

MySQL创建member表失败

最近在做一个项目&#xff0c;在台式机上可以跑通&#xff0c;也测试了各个已完成的接口&#xff0c;提交到了GitHub后想着用宿舍的电脑跑一下&#xff0c;在测试member表相关接口时就出错了。报了SQL语法错误&#xff0c;但SQL语句很简单&#xff0c;就根据手机号查询不至于出…...

使用minio实现大文件断点续传

部署 minio 拉取镜像 docker pull minio/minio docker images新建映射目录 新建下面图片里的俩个目录 data(存放对象-实际的数据) config 存放配置开放对应端口 我使用的是腾讯服务器所以 在腾讯的安全页面开启 9000&#xff0c;9090 两个端口就可以了&#xff08;根据大家实际…...

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…...

Tomcat日志乱码了怎么处理?

【前言】 tomacat日志有三个地方&#xff0c;分别是Output(控制台)、Tomcat Localhost Log(tomcat本地日志)、Tomcat Catalina Log。 启动日志和大部分报错日志、普通日志都在output打印;有些错误日志&#xff0c;在Tomcat Localhost Log。 三个日志显示区&#xff0c;都可能…...

[node] Node.js的路由

[node] Node.js的路由 路由 & 路由解析路由信息的整合URL信息路由处理逻辑路由逻辑与URL信息的整合路由的使用 路由 & 路由解析 路由需要提供请求的 URL 和其他需要的 GET/POST 参数&#xff0c;随后路由需要根据这些数据来执行相应的代码。 因此&#xff0c;根据 HT…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...