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

【性能优化】安卓性能优化之CPU优化

【性能优化】安卓性能优化之CPU优化

      • CPU优化及常用工具
        • 原理与文章参考
        • 常用ADB
        • 常用原理、监控手段
          • 原理
          • 监控手段
          • 多线程并发解决耗时
          • UI相关
        • 常见场景
        • 排查CPU占用过高
        • 常用系统/开源分析工具
          • AndroidStudio Profiler
          • Systrace
          • Btrace
          • Perfetto
          • TraceView和 Profile
      • ANR相关
      • CPU 优化案例

CPU优化及常用工具

原理与文章参考
  • 编舞者、looper、JankStats方法
常用ADB
含义命令备注
查看CPU状态adb shell top -H -d 1 -p pid -O cpu-O cpu 查看对应在那个核心 ;修改采样间隔为1s
导出当前进程所有线程状态到tombstonedadb shell run-as kill -3 实际上是以一个异常状态导出了,利用了墓碑机制
查看进程的所有线程adb shell "ps -Tgrep pid"
查看进程占用cpu情况adb shell dumpsys cpuinfogrep [进程名]
查看进程内线程占用cpu的情况adb shell top -n 1 -d 0.5grep proc_ id
获取设备cpu信息adb shell cat /proc/cpuinfo或者查看 /sys/devices/system/cpu 目录下的文件夹
常用原理、监控手段
原理
  1. 普通手机默认60帧刷新率,相当于每帧16.6ms
  2. 利用系统预留接口 对每个帧率/handler消息等 进行统计
监控手段
  1. 设置looperPrinter
  2. 字节码插桩检测慢函数(martix dokit)
  3. 编舞者获取frame帧率
  4. jetpack JankStats,获取丢帧信息
多线程并发解决耗时

线程池/数量参考

  1. CPU密集:线程数设置为CPU核心数 + 1
  2. IO密集:线程数设置为CPU核心数 * 2
UI相关
  • 利用<font style="color:rgb(77, 77, 77);">IdelHandler</font>对一些常用view进行预绘制
  • 通过排查布局,减少过度绘制
常见场景
  • 过度绘制
  • 频繁IO
  • 主线程耗时任务
排查CPU占用过高
  • 规范线程命名,定位线程
  • 抓取top数据,查看具体哪个线程占用高
  • cpu指标含义解释
  • 线程各参数详解
常用系统/开源分析工具
AndroidStudio Profiler
  • 抓取CPU火焰图,卡顿/ANR 主要监测主线程,是否会出现耗时操作
Systrace
  • 官方指令参考
  • 官方推荐指令 $ python systrace.py -o mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
  • 要求环境

python2.7 安装
python six 模块,命令 : pip install six
“No module named win32con” 问题,安装相关: pip install pypiwin32

  • 拉取到信息后用perfetto 打开即可 但是这个主要是针对系统的 对应用开发帮助不大,分析自己应用可以用btrace
Btrace
  • 官方链接
Perfetto
  • 官方-快速开始
  • 工具界面
  • 入门使用
  • 线程状态

TraceView和 Profile
  • traceview官方参考
  • traceview使用
  • 导出的日志分析
  • 使用DDMS查看

新版路径:Sdk\tools\monitor.bat

  • Incl Cpu Time:方法在CPU中执行所有时间(包含其调用的方法所消耗的时间)
  • Excl Cpu Time: 方法在CPU中执行的时间(不包含其调用的方法所消耗的时间)
  • Incl Real Time:方法运行消耗的所有时间(包含子方法)
  • Excl Real Time:方法运行消耗的时间(不包含子方法)
  • Calls + Recur Calls/Total :方法调用、递归次数(重要指标,防止死循环)
  • Cpu Time/Call :该方法平均占用 CPU 的时间(重要指标,可以看出单个方法占用CPU的平均时间,但是要防止在个别调用处出现长时间占用,然后被平均了)
  • Real Time/Call :平均执行时间,包括切换、阻塞的时间(重要指标,可以看出单个方法执行的平均时间值,但是要防止在个别调用处出现长时间调用,然后被平均了)
  • TraceView优势

可以精确埋点

Debug.startMethodTracing("sample");...
Debug.stopMethodTracing();

ANR相关

ANR原理及常见场景
  • 原理

ANR(Application Not Responding)的监测原理本质上是消息机制,设定一个delay消息,超时未被移除则触发ANR。具体逻辑处理都在system server端,包括发送超时消息,移除超时消息,处理超时消息以及ANR弹框展示等;对于app而言,触发ANR的条件是主线程阻塞。

  • 常见场景
  1. Service ANR:前台20s,后台200s;startForeground超时10s
  2. Broadcast ANR:前台10s,后台60s
  3. Input ANR:按键或触摸事件在5s内无响应
  4. ContentProvider ANR:10s,少见
ANR/卡顿检测
  • 通过设置Looper的printer可以检测耗时
  • WatchDog机制,子线程发送消息自增,休眠后检查
  • 参考
  • ANR日志导出
// 安卓21以下有权限可以获取到 anr 日志
private FileObserver fileObserver = null;
void initialize(....){// 实例化FileObserver ,监控路径"/data/anr/",监听文件被写入fileObserver = new FileObserver("/data/anr/", CLOSE_WRITE) {public void onEvent(int event, String path) {try {if (path != null) {String filepath = "/data/anr/" + path;// 写入的文件是否有关键字 “trace”if (filepath.contains("trace")) {// 处理anr异常handleAnr(filepath);}}} catch (Exception e) {XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver onEvent failed", e);}}};try {// 启动FileObserver 监控fileObserver.startWatching();} catch (Exception e) {fileObserver = null;XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver startWatching failed", e);}
}private void handleAnr(String filepath) {...// 读取anr文件 /data/anr/trace*.txt。返回文件内容String trace = getTrace(filepath, anrTime.getTime());//删除其他的anr异常日志文件if (!FileManager.getInstance().maintainAnr()) {return;}//获取  tombstone 的文件头String emergency = null;try {emergency = getEmergency(anrTime, trace);} catch (Exception e) {XCrash.getLogger().e(Util.TAG, "AnrHandler getEmergency failed", e);}// 创建anr异常日志保存文件File logFile = null;try {String logPath = String.format(Locale.US, "%s/%s_%020d_%s__%s%s", logDir, Util.logPrefix, anrTime.getTime() * 1000, appVersion, processName, Util.anrLogSuffix);logFile = FileManager.getInstance().createLogFile(logPath);} catch (Exception e) {XCrash.getLogger().e(Util.TAG, "AnrHandler createLogFile failed", e);}if (logFile != null){// 根据配置将日志文件头,traces,logcat日志保存在文件中。}
}// 高版本通过AMS获取日志
public class ANRMoniter implements Runnable {private final String TAG = "ANRMoniter";private HandlerThread handlerThread = new HandlerThread("WatchMainHandler");private ILog logImpl;private Application app;private Handler watchHandler;private Handler mainHandler;private ScheduleCheckTask scheduleCheckTask;private int CHECK_INTERVAL = 5_000;public ANRMoniter(Application app, ILog logImpl) {this.app = app;this.logImpl = logImpl;init();}private void init() {handlerThread.start();Looper looper = handlerThread.getLooper();watchHandler = new Handler(looper);mainHandler = new Handler(Looper.getMainLooper());scheduleCheckTask = new ScheduleCheckTask();}public void start() {watchHandler.post(this);}@Overridepublic void run() {mainHandler.post(scheduleCheckTask);long endTime = System.currentTimeMillis() + CHECK_INTERVAL;long sleepTime = endTime - System.currentTimeMillis();while (sleepTime > 0) {try {Thread.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}sleepTime = endTime - System.currentTimeMillis();}if (scheduleCheckTask.isBlocking()) {logImpl.Loge(TAG,"main handler blocking");checkRealANR(mainHandler.getLooper().getThread().getStackTrace());}scheduleCheckTask.reset();watchHandler.post(this);}private void checkRealANR(StackTraceElement[] stack) {ThreadPool.getInstance().execute(new Runnable() {@Overridepublic void run() {ActivityManager.ProcessErrorStateInfo processErrorStateInfo = getANRInfo(app);if (processErrorStateInfo != null) {logImpl.Loge(TAG,"ANR action");//real ANRRuntimeException e = new RuntimeException(processErrorStateInfo.shortMsg);e.setStackTrace(stack);e.printStackTrace();logImpl.Loge(TAG,e.getMessage());}}});}private ActivityManager.ProcessErrorStateInfo getANRInfo(Application app) {try {final long sleepTime = 500L;final long loop = 20;long times = 0;do {ActivityManager activityManager = (ActivityManager) app.getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.ProcessErrorStateInfo> processesInErrorState = activityManager.getProcessesInErrorState();if (processesInErrorState != null) {for (ActivityManager.ProcessErrorStateInfo proc : processesInErrorState) {if (proc.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {return proc;}}}Thread.sleep(sleepTime);} while (times++ < loop);} catch (Exception e) {e.printStackTrace();}return null;}private class ScheduleCheckTask implements Runnable {private boolean isBlocking;ScheduleCheckTask() {isBlocking = true;}@Overridepublic void run() {isBlocking = false;}public boolean isBlocking() {return isBlocking;}public void reset() {isBlocking = true;}}
}
  • 自定义线程WatchDog参考
package com.aispeech.util;import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;import com.aispeech.common.ThreadNameUtil;
import com.aispeech.lite.BaseKernel;import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/**
* Description: 检测Kernel层 是否阻塞的工具类
* Author: junlong.huang
* CreateTime: 2023/8/21
*/
public class KernelWatchDog {private static final String TAG = "KernelWatchDog";HandlerThread innerThread;Handler innerHandler;long timeoutMillis = 2000;static final int MSG_INCREMENT = 0x01;private static volatile KernelWatchDog mInstance;private ConcurrentHashMap<BaseKernel, AtomicInteger> monitorMap;private Vector<BaseKernel> removeList;public static KernelWatchDog getInstance() {if (mInstance == null) {synchronized (KernelWatchDog.class) {if (mInstance == null) {mInstance = new KernelWatchDog();}}}return mInstance;}private KernelWatchDog() {init();}private void init() {monitorMap = new ConcurrentHashMap<>();removeList = new Vector<>();innerThread = new HandlerThread(ThreadNameUtil.getSimpleThreadName("watchdog-k"));innerThread.start();innerHandler = new InnerHandler(innerThread.getLooper());innerHandler.sendMessage(innerHandler.obtainMessage(MSG_INCREMENT));}public void addChecker(BaseKernel baseKernel) {Log.i(TAG, "addChecker:" + baseKernel.getInnerThreadName());monitorMap.put(baseKernel, new AtomicInteger(baseKernel.getTick()));}public void removeChecker(BaseKernel baseKernel) {if (monitorMap.containsKey(baseKernel)) {Log.i(TAG, "removeChecker: " + baseKernel.getInnerThreadName());monitorMap.remove(baseKernel);}}class InnerHandler extends Handler {public InnerHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (innerHandler == null) return;switch (msg.what) {case MSG_INCREMENT:if (monitorMap == null || monitorMap.size() == 0) {innerHandler.sendMessageDelayed(innerHandler.obtainMessage(MSG_INCREMENT), timeoutMillis);break;}for (BaseKernel baseKernel : monitorMap.keySet()) {if (baseKernel.getInnerThread() != null &&!baseKernel.getInnerThread().isAlive()) {Log.i(TAG, "Detected thread quit,Add to list to be removed");removeList.add(baseKernel);continue;}AtomicInteger lastTick = monitorMap.get(baseKernel);if (lastTick == null) lastTick = new AtomicInteger(baseKernel.getTick());if (lastTick.get() != baseKernel.getTick()) {Log.w(TAG, "Detected target thread may blocked,export thread stack");Thread innerThread = baseKernel.getInnerThread();if (innerThread != null) {Log.w(TAG, getThreadStack(innerThread.getStackTrace()));}}lastTick.incrementAndGet();baseKernel.tick();}for (BaseKernel baseKernel : removeList) {monitorMap.remove(baseKernel);}removeList.clear();innerHandler.sendMessageDelayed(innerHandler.obtainMessage(MSG_INCREMENT), timeoutMillis);break;}}}public void release() {innerHandler.removeMessages(MSG_INCREMENT);innerThread.quit();monitorMap.clear();removeList.clear();}private String getThreadStack(StackTraceElement[] elements) {StringBuilder stackTraceString = new StringBuilder();for (StackTraceElement element : elements) {stackTraceString.append(element.toString()).append("\n");}return stackTraceString.toString();}}
卡顿检测
  • matrix 字节码插桩,慢函数检测
  • 采样率法,通过一个外置的工作线程Handler,按一段时间采样,如果大部分都是某个方法,则这个方法可能存在风险点
/**
* 按照一定频率采样
* 目标是找到卡顿时刻前后的堆栈,做大致定位,无法做到精准定位
* 原则上采样越高,定位越精准
* 还有,目前只采样了java层的堆栈,c层的需要另外实现,这个后续补充
*/
public class CallstackSampler {private static final String TAG = "CallstackSampler";private final Thread thread;private final Handler mHandler;private final long sThreshold = 1000;private final Runnable mRunnable = new Runnable() {@Overridepublic void run() {doSample();mHandler.postDelayed(this, sThreshold);}};public CallstackSampler(Thread thread) {this.thread = thread;HandlerThread mWorkThread = new HandlerThread("StackSampler" + thread.getName());mWorkThread.start();mHandler = new Handler(mWorkThread.getLooper());}private void doSample() {// 采集指定线程当前堆栈信息StackTraceElement[] stackTrace = thread.getStackTrace();String stackTraceString = Arrays.toString(stackTrace);if (!stackTraceString.contains("nativePollOnce")) {Log.d(TAG, thread.getName() + " Callstack sample taken at time: " + System.currentTimeMillis() + " " + stackTraceString);}}public void startSampling() {mHandler.postDelayed(mRunnable, sThreshold);}public void stopSampling() {mHandler.removeCallbacks(mRunnable);}
}
  • 主线程耗时检测:设置一个printer

CPU 优化案例

  1. 线程池复用,减少CPU调度开销
  2. 资源拷贝优化,减少读取IO时间
  3. 线程命名,方便定位问题
  4. 非必要内容,延迟初始化
  5. 初始化任务优先级分配,削峰填谷

相关文章:

【性能优化】安卓性能优化之CPU优化

【性能优化】安卓性能优化之CPU优化 CPU优化及常用工具原理与文章参考常用ADB常用原理、监控手段原理监控手段多线程并发解决耗时UI相关 常见场景排查CPU占用过高常用系统/开源分析工具AndroidStudio ProfilerSystraceBtracePerfettoTraceView和 Profile ANR相关ANR原理及常见场…...

springboot二手图书交易系统-计算机设计毕业源码88413

目 录 摘要 1 绪论 1.1研究背景 1.2研究意义 1.3论文结构与章节安排 2 二手图书交易系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 二手…...

解决ElasticSearch启动成功却无法在浏览器访问问题

目录 前言&#xff1a; 问题复现 &#xff1a; 解决问题&#xff1a; 1、修改sysctl.conf文件 2、在sysctl.conf文件增加这段东西 3、 然后保存退出&#xff0c;输入以下命令使其生效 结语&#xff1a; 前言&#xff1a; 这篇文章是小白我今天突然启动es&#xff0c;发现e…...

稀土有色包芯线良好的导电性

稀土有色包芯线是一种结合了稀土元素和有色金属(如铜、铝、镁等)的特殊线材。以下是对稀土有色包芯线的详细介绍&#xff1a; 一、组成与结构 芯线&#xff1a;由稀土元素和有色金属组成的合金制成。稀土元素(如镧、铈、镁等)的添加量在一定范围内&#xff0c;以确保合金性能的…...

SIP 业务举例之 Call Forwarding Unconditional(无条件呼转)

目录 1. Call Forwarding Unconditional 简介 2. RFC5359 的 Call Forwarding Unconditional 信令流程 PS:Dialog 建立条件 Dialog 会话完全建立 3. Call Forwarding Unconditional 过程总结 博主wx:yuanlai45_csdn 博主qq:2777137742 想要 深入学习 5GC IMS 等通信知识…...

基于stm32的esp8266的WIFI控制风扇实验

实验案例&#xff37;&#xff29;&#xff26;&#xff29;控制风扇 项目需求 电脑通过esp8266模块远程遥控风扇。 项目框图 ​ 风扇模块封装 #include "sys.h" #include "fan.h"void fan_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟…...

java中的ScheduledExecutorService介绍和使用案例

ScheduledExecutorService 是 Java 并发包 java.util.concurrent 中的一个接口&#xff0c;它提供了一种机制&#xff0c;允许我们安排一个任务在给定的延迟后运行&#xff0c;或者定期地执行。 主要特点 单次调度&#xff1a;可以安排任务在一定的延迟后执行一次。周期性调度…...

4天涨粉14万!这个AI小众赛道粉丝涨疯了吧?保姆级教程免费教会你!

测一下你的搞钱灵敏度有多高&#xff0c;看下面两张截图&#xff0c;有没有发现什么异常值&#xff1f; 发现了吧&#xff1f; 第一张是10月17号截的&#xff0c;第二张是21号&#xff0c;4天时间粉丝从2.8万飙到16.6万&#xff0c;涨粉14万&#xff01; 这个号我几天之前就发…...

RK3588 技术分享 | 在Android系统中使用NPU实现Yolov5分类检测

随着人工智能和大数据时代的到来&#xff0c;传统嵌入式处理器中的CPU和GPU逐渐无法满足日益增长的深度学习需求。为了应对这一挑战&#xff0c;在一些高端处理器中&#xff0c;NPU&#xff08;神经网络处理单元&#xff09;也被集成到了处理器里。NPU的出现不仅减轻了CPU和GPU…...

itext 转换word文档转pdf

itext 转换word文档转pdf <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version><scope>compile</scope></dependency> <dependency><groupId>org.a…...

WSL-默认root登录

WSL-默认root登录 使用管理员&#xff0c;打开powershell PS C:\WINDOWS\system32> wsl -l 适用于 Linux 的 Windows 子系统分发版: Ubuntu-22.04 (默认) PS C:\WINDOWS\system32> ubuntu2204.exe config --default-user root PS C:\WINDOWS\system32>修改之后&…...

ASIO网络调试助手之四:浅谈QTcpServer性能

网络上有些质疑Qt Network模块性能的声音,本文将从理论和压测两个方面对比ASIO tcp server和QTcpServer在Windows上的性能,最后给出结论。 一.理论 QTcpServer在Windows上的实现是基于select模型,源码位置:5.12.6\Src\qtbase\src\network\socket\qnativesocketengine_win…...

快手 日常实习一面面经

官网投递&#xff0c;三天约面 线上面试 &#xff08;60mins&#xff09;&#xff1a; 1. 自我介绍 2. 问实习 介绍自己做的业务上一段实习的公司框架和开源的 RPC 框架的区别负责的业务与核心业务的依赖关系场景题 -> 设计高并发下的负载均衡 3. 项目拷打 手写 双检锁…...

Cursor零基础小白教程系列 - 创建你的第一个Cursor 项目

最适合小白零基础的Cursor教程 网站lookai.top相同作者&#xff0c;最新文章会在网站更新&#xff0c;欢迎收藏书签 创建你的第一个Cursor 项目 实操视频 概述 开始使用Cursor进行编程的第一步是创建或导入一个项目。本指南将帮助您了解如何在Cursor中创建新项目、导入现有项…...

通用序列化和反序列化实现思路

本文简单的记录一下采用模板来实现序列化与反序列的思路&#xff0c; 同时采用C20标准的concept和requires来简化模板函数的选择。 首先了解一下自定义类支持序列化的两种方式&#xff1a; 一、序列化自定义类型&#xff08;侵入式&#xff09; struct Test {std::string na…...

书生营第四期L0G2000 Python 基础知识

闯关任务 Leetcode 383(笔记中提交代码与leetcode提交通过截图) class Solution:def canConstruct(self, ransomNote: str, magazine: str) -> bool:note [0]*26maga [0]*26for s in ransomNote:note[ord(s)-ord(a)] 1for s in magazine:maga[ord(s)-ord(a)] 1for i in…...

Day12-数据库服务冗余架构

Day-12-数据库服务高可用集群 1、数据库MGR组复制实践2、数据库高可用MHA应用介绍3、数据库高可用MHA环境准备4、数据库高可用MHA原理机制5、数据库高可用MHA功能配置 1、数据库MGR组复制实践 &#xff08;强一致性主从同步) 2、数据库高可用MHA应用介绍 3、数据库高可用MHA环境…...

js监听div尺寸,ResizeObserver

示例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><style>.observedDiv {width: 40vw;height: 50vh;background-color: lightblue;}</style></head><body><div id"…...

STM32与openmv的串口通信

OpenMV与STM32的通信是嵌入式系统和物联网领域中的一项重要技术。OpenMV是一种开源的微型机器视觉模块&#xff0c;基于ARM Cortex-M7微控制器&#xff0c;支持多种图像处理功能&#xff0c;如颜色识别、形状检测等。而STM32是STMicroelectronics公司推出的基于ARM Cortex内核的…...

C#基于SkiaSharp实现印章管理(11)

PdfSharpCore支持类似GDI方式在PDF页面绘制文字、矩形、圆形、多边形、路径、图片等内容&#xff0c;本文学习基于PdfSharpCore将结构化印章数据导出为PDF文件的基本用法&#xff0c;评估其使用可行性。   PdfSharpCore创建PDF文件很方便&#xff0c;调用PdfDocument类创建实…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...