聊聊springboot项目如何利用jmh来进行基准测试

前言

1、什么是JMH

JMH(Java Microbenchmark Harness)是由OpenJDK团队开发的一个用于Java微基准测试工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。它提供了一种标准、可靠且可重复的方式来衡量Java代码的性能,包括方法调用、对象创建以及其他类型的 JVM 级别的操作。JMH 通过生成优化过的字节码来确保基准测试不受常见陷阱的影响,如热身不足、垃圾回收干扰、编译器优化等,从而产生更准确的性能指标

2、JMH主要使用场景

  • 精确测量方法执行时间: 当你需要准确知道某个特定Java方法或代码段在不同输入、不同环境条件下的执行时间时,可以使用JMH进行基准测试。例如,你可能想比较不同字符串连接方法(如String.concat()与StringBuilder.append())的性能差异。
  • 吞吐量对比: 在评估接口实现或者算法效率时,JMH可以帮助你对比不同实现在相同工作负载下的吞吐量,即单位时间内能够处理的任务数量。
  • 响应时间和分布分析: JMH不仅提供平均执行时间的数据,还可以帮助分析请求完成的时间分布情况,比如你可以了解到多少百分比的请求能在多长时间内完成。
  • 性能优化验证: 在对代码进行性能优化后,使用JMH进行基准测试可以量化改进前后的性能差异,确保优化措施确实提高了程序的运行效率。
  • 并发和并行性能评估: 对于涉及多线程和并发操作的代码块,JMH提供了强大的工具来测量在不同并发级别下系统的性能表现。
  • JVM行为研究: 由于JMH深入到JVM层面进行测试,并且能控制垃圾收集、编译器优化等因素的影响,它对于理解JVM如何影响代码性能以及研究内存分配、垃圾回收策略等具有重要意义。
  • 跨平台可比性: 使用JMH可以在不同的Java版本、不同的操作系统和硬件配置上得到相对可比的基准测试结果,有助于在多种环境下评估代码性能的一致性。

3、JMH常用注解

注: 因为我们主要利用JMH提供的注解来进行基准测试,因此我们有必要了解一下JMH一些常用注解

@State: 表明类的所有属性的作用域。只能用于类上。它有如下选项

  • Scope.Thread: 默认的State,每个测试线程分配一个实例;
  • Scope.Benchmark: 所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;
  • Scope.Group: 每个线程组共享一个实例;

@BenchmarkMode: 用于指定基准测试的执行模式,如吞吐量、平均执行时间。可用于类或者方法上,它有如下模式

  • Throughput:整体吞吐量,每秒执行了多少次调用,单位为 ops/time
  • AverageTime:用的平均时间,每次操作的平均时间,单位为 time/op
  • SampleTime:随机取样,最后输出取样结果的分布
  • SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
  • All:上面的所有模式都执行一次

@Measurement: 用于控制压测的次数、时间和批处理数量。可用于类或者方法上,它有如下参数

  • iterations:测量的次数
  • time:每次测量持续的时间
  • timeUnit:时间的单位,默认秒
  • batchSize:批处理大小,每次操作调用几次方法

@Warmup: 预热,可用于类或者方法上

由于JVM会使用JIT对热点代码进行编译,因此同一份代码可能由于执行次数的增加而导致执行时间差异太大,因此我们可以让代码先预热几轮,预热时间不算入测量计时。@WarmUp 的使用和 @Measurement 一致。

@Fork: 用于指定fork出多少个子进程来执行同一基准测试方法,可用于类或者方法上。例如@Fork指定数量为2,则 JMH 会 fork 出两个进程来进行测试

@Threads: 用于指定使用多少个线程来执行基准测试方法,可用于类或者方法上。例如@Threads 指定线程数为 2 ,那么每次测量都会创建两个线程来执行基准测试方法

@OutputTimeUnit: 可以指定输出的时间单位,可用于类或者方法注解

@Param: 指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,使用该注解必须定义 @State 注解。

@Setup: 用于基准测试前的初始化动作,只能用于方法

@TearDown 用于基准测试后执行,主要用于资源的回收,只能用于方法

4、JMH陷阱

常见的比如死码消除。所谓的死码,是指注释的代码,不可达的代码块,可达但不被使用的代码等等。如示例下例子

 @Benchmarkpublic void testMethod() {int a = 1;int b = 2;int sum = a + b;}

JVM可以检测到分配给sum的a+b的计算从未被使用。因此,JVM可以完全取消a+b的计算。它被认为是死代码。JVM然后可以检测到sum变量从未被使用,并且随后a和b也从未被使用。他们也可以被淘汰。上面的例子最终会被优化成

 @Benchmarkpublic void testMethod() {}

这样会影响测试结果。JMH提供了如下两种方法来避免死码。一种是将变量当成返回值返回。示例

 @Benchmarkpublic int testMethod() {int a = 1;int b = 2;int sum = a + b;return sum;}

一种是利用Blackhole 的 consume 来避免 JIT 的优化消除。
示例:

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;public class MyBenchmark {@Benchmarkpublic void testMethod(Blackhole blackhole) {int a = 1;int b = 2;int sum = a + b;blackhole.consume(sum);}
}

其他陷阱还有常量折叠与常量传播、永远不要在测试中写循环、使用 Fork 隔离多个测试方法、方法内联、伪共享与缓存行、分支预测、多线程测试等,感兴趣的朋友可以阅读
https://github.com/lexburner/JMH-samples
了解全部的陷阱。

正文

通过前面的铺垫,大家对jmh应该有个大致的了解,接下来我们就来演示一下springboot项目如何利用jmh进行基准测试

1、springboot的项目中引入JMH GAV

 <properties><jmh.version>1.36</jmh.version></properties><dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>${jmh.version}</version></dependency><dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>${jmh.version}</version><scope>provided</scope></dependency>

2、编写测试

注: 因为有前面的铺垫介绍,因此下面的例子大家应该比较容易看得懂,就不再论述,直接上代码

@Measurement(iterations = 2, time = 10)
@Warmup(iterations = 2, time = 10)
@Fork(1)
@Threads(value = 2)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.All)
@OutputTimeUnit(TimeUnit.SECONDS)
public class SpringBootJmhTest {private ConfigurableApplicationContext context;private MockBizService mockBizService;/*** @Param 允许使用一份基准测试代码跑多组数据,特别适合测量方法性能和参数取值的关系*/@Param({"100","500","1"})public long mockBizQueryTime;/***   注意要使用 run模式启动main函数,不要使用debug模式启动。*  否则会报错:transport error 202: connect failed: Connection refused ERROR* @param args* @throws RunnerException*/public static void main(String[] args) throws RunnerException {String report = DateUtil.today() + "-jmhReport.json";Options opt = new OptionsBuilder().include(SpringBootJmhTest.class.getSimpleName())// 参数优先级顺序:类 < 方法 < Options// 因此如下配置会覆盖@Warmup配置.warmupIterations(1).warmupTime(TimeValue.seconds(5))//报告输出.可以将结果上传到 https://jmh.morethan.io 或者/http://deepoove.com/jmh-visual// 进行分析.result(report)//报告格式.resultFormat(ResultFormatType.JSON).build();new Runner(opt).run();}/*** @Setup 用于基准测试前的初始化动作** Level参数表明粒度,粒度从粗到细分别是** Level.Trial:Benchmark级别* Level.Iteration:执行迭代级别* Level.Invocation:每次方法调用级别*/@Setup(Level.Trial)public void setUp(){context = SpringApplication.run(SpringBootJmhApplication.class);mockBizService = context.getBean(MockBizService.class);}/**** @Benchmark 来标记需要基准测试的方法.该方法需要为public* @param blackhole 的作用是:防止无用代码被JVM优化导致的基准测试结果不准确*/@Benchmarkpublic void testMockBizService(Blackhole blackhole) {blackhole.consume(mockBizService.query(mockBizQueryTime));}/*** @TearDown 用于基准测试后执行*/@TearDownpublic void tearDown() {context.close();}}

3、运行JMH

运行的方式常见有如下几种,一种是直接运行main函数

如示例

 /***   注意要使用 run模式启动main函数,不要使用debug模式启动。*  否则会报错:transport error 202: connect failed: Connection refused ERROR* @param args* @throws RunnerException*/public static void main(String[] args) throws RunnerException {String report = DateUtil.today() + "-jmhReport.json";Options opt = new OptionsBuilder().include(SpringBootJmhTest.class.getSimpleName())// 参数优先级顺序:类 < 方法 < Options// 因此如下配置会覆盖@Warmup配置.warmupIterations(1).warmupTime(TimeValue.seconds(5))//报告输出.可以将结果上传到 https://jmh.morethan.io 或者/http://deepoove.com/jmh-visual// 进行分析.result(report)//报告格式.resultFormat(ResultFormatType.JSON).build();new Runner(opt).run();}

执行main方法,记得需要使用run模式运行,如图

而不是以debug模式,否则会报

transport error 202: connect failed: Connection refused ERROR

另一种是IDE安装JMH插件,以idea为例,在plugins搜索JMH,然后安装插件,安装成功后,可以像执行单元测试那种,单独运行加@Benchmark注解的方法
示例

可以点击圈红的小图标运行,也可以选中加了@Benchmark的方法,右键run运行


还有一种是直接打成jar运行

打成jar包也有如下两种方式。一种在项目的pom引入相应的打包插件

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.1</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><finalName>springboot-jmh</finalName><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformerimplementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer"><resource>META-INF/spring.factories</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>com.github.lybgeek.jmh.SpringBootJmhTest</mainClass></transformer></transformers></configuration></execution></executions></plugin></plugins>
</build>

这种插件的注意点是main函数直接指定我们要进行基准测试的main函数的类,比如

com.github.lybgeek.jmh.SpringBootJmhTest

其次因为我们springboot运行会依赖一些自动装配,因此我们也需要将相关的配置比如spring.factories装载进去。不然打包的时候可能会报

Cannot find 'resource' in class org.apache.maven.plugins.shade.resource.ManifestResourceTransformer

不过这只是其中一种解法,下边我后讲解另一种解法。

运行如下命令

mvn clean package
java -jar springboot-jmh.jar  -rf json -rff D:/jmhResult.json

其中**-rf:** 为输出的格式为json -rff: 为指定输出的位置

另外一种直接引入官方提供示例插件,该插件也是shade插件,只是此时mainclass为

org.openjdk.jmh.Main
  <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.1</version><executions><execution><id>shade-my-jar</id><phase>package</phase><goals><goal>shade</goal></goals><configuration><finalName>springboot-jmh</finalName><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>org.openjdk.jmh.Main</mainClass></transformer></transformers></configuration></execution></executions></plugin></plugins>

这边有个小细节,是因为springboot本身也有依赖shade插件,因此我们自己的shade插件要指定id。如示例配置

 <plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><executions><execution><id>shade-my-jar</id>
...

否则会和springboot默认的插件id冲突,而导致出现

Cannot find 'resource' in class org.apache.maven.plugins.shade.resource.ManifestResourceTransformer

配置完成后打包,并运行如下命令

java -jar springboot-jmh.jar SpringBootJmhTest  -rf json -rff D:/jmhResult.json

注: SpringBootJmhTest 为我们要进行JMH测试的类

以上几种执行方式如何取舍

如果是小测试,直接通过main函数或者jmh插件运行即可。如果是比较大的测试,测试时间比较长,且需要可能需要比较多的资源,可以打成jar测试

4、查看测试结果

Benchmark                                                        (mockBizQueryTime)    Mode  Cnt   Score    Error  Units
SpringBootJmhTest.testMockBizService                                            100   thrpt    2  18.554           ops/s
SpringBootJmhTest.testMockBizService                                            500   thrpt    2   3.935           ops/s
SpringBootJmhTest.testMockBizService                                              1   thrpt    2   1.986           ops/s
SpringBootJmhTest.testMockBizService                                            100    avgt    2   0.108            s/op
SpringBootJmhTest.testMockBizService                                            500    avgt    2   0.509            s/op
SpringBootJmhTest.testMockBizService                                              1    avgt    2   1.005            s/op
SpringBootJmhTest.testMockBizService                                            100  sample  370   0.108 ±  0.001   s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.00                   100  sample        0.103            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.50                   100  sample        0.108            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.90                   100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.95                   100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.99                   100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.999                  100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.9999                 100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p1.00                   100  sample        0.109            s/op
SpringBootJmhTest.testMockBizService                                            500  sample   78   0.507 ±  0.001   s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.00                   500  sample        0.500            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.50                   500  sample        0.508            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.90                   500  sample        0.510            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.95                   500  sample        0.511            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.99                   500  sample        0.513            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.999                  500  sample        0.513            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.9999                 500  sample        0.513            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p1.00                   500  sample        0.513            s/op
SpringBootJmhTest.testMockBizService                                              1  sample   38   1.005 ±  0.003   s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.00                     1  sample        0.999            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.50                     1  sample        1.002            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.90                     1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.95                     1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.99                     1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.999                    1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p0.9999                   1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService:testMockBizService·p1.00                     1  sample        1.014            s/op
SpringBootJmhTest.testMockBizService                                            100      ss    2   0.110            s/op
SpringBootJmhTest.testMockBizService                                            500      ss    2   0.508            s/op
SpringBootJmhTest.testMockBizService                                              1      ss    2   1.010            s/op

报告的参数解读如下

Mode: 模式

  • thrpt:吞吐量
  • avgt:每次请求的平均耗时
  • sample:请求样本数量,这次压测一共发了多少个请求
  • ss:除去冷启动,一共执行了多少轮

Cnt: 基准测试执行的迭代次数或者样本数量

Score: 是性能测试结果的主要度量单位。它代表了基准测试方法的吞吐量或者执行速度,具体含义取决于你选择的@BenchmarkMode。

例如你设置了 @BenchmarkMode(Mode.Throughput),那么 Score 将表示每秒可以执行该操作的次数(ops/s),即吞吐量。 - 若设置为 @BenchmarkMode(Mode.AverageTime),则 Score 表示的是平均每个操作所需的时间(如ns/op、ms/op等),数值越小通常意味着性能越好

Errors: 通常指的是执行过程中统计性能指标时的误差范围。由于JMH基于统计学原理进行性能测量,因此其结果会受到随机性和系统噪声的影响

Units: 通常指的是度量基准测试结果时使用的单位。根据你选择的@BenchmarkMode不同,报告中的单位也会有所变化

5、jmh测试结果可视化

我们可以将生成jmh的json结果上传到如下网站,进行可视化分析

  • JMH Visual Chart:hhttp://deepoove.com/jmh-visual-chart/
  • Visualizer:https://jmh.morethan.io/

总结

本文主要大致讲下如何使用jmh。jmh的详细案例,可以查看官网
https://github.com/openjdk/jmh
或者查看下面博主写的文章
https://cloud.tencent.com/developer/article/1760933

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-jmh

参考文档

https://jenkov.com/tutorials/java-performance/jmh.html
https://www.cnblogs.com/wupeixuan/p/13091381.html

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

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

相关文章

【深度学习(42)】通过vscode使用anaconda的python环境

按ctrlshiftp&#xff0c;选择Python:Select Interpreter 选择anaconda下的python虚拟环境

大模型备案全网最详细流程说明【附附件】

下图为最新的直至第五批深度合成服务算法备案信息的公告 根据目前公开的国内大模型算法备案统计来看&#xff0c;首批境内深度合成服务算法备案清单&#xff0c;总共通过了五批。 以第二批举例&#xff0c;境内深度合成服务算法备案清单&#xff0c;总共通过110家&…

Python的异常处理(与C++对比学习)

一、C语言中错误的处理方式 用assert来判断一个表达式是否出错&#xff1b;在调用接口函数时&#xff0c;接口函数会设置errno&#xff0c;我们可以通过errno&#xff0c;strerror(errno)来拿到错误码和错误信息。在自定义函数中&#xff0c;我们设置函数错误信息处理的时候&a…

告别堆积,迎接清新:回收小程序,打造无废生活新选择

在快节奏的现代生活中&#xff0c;物质的丰富与便利似乎成为了我们日常的一部分&#xff0c;但随之而来的&#xff0c;是日益增长的废弃物堆积问题。街道边、社区里&#xff0c;甚至是我们的家中&#xff0c;废弃物品仿佛无孔不入&#xff0c;逐渐侵蚀着我们的生活空间与环境质…

基于Netty的自研流系统缓存实现挑战: 内存碎片与OOM困境

01 前言 Kafka 作为流处理平台&#xff0c;在实时流计算和在线业务场景&#xff0c;追尾读追求端到端低延迟。在离线批处理和削峰填谷场景&#xff0c;数据冷读追求高吞吐。两个场景都需要很好的数据缓存设计来支撑&#xff0c;Apache Kafka 的数据存储在本地文件&#xff0c…

pointnet2_ops_lib/.安装报错解决方案

问题 3D点云相关的代码例如pointnn、pointmlp都需要安装pointnet2_ops&#xff0c;可是基本上在安装pointnet2_ops时总会报错&#xff0c;终归原因是虚拟环境的cuda版本和安装的torch&#xff0c;torchvision&#xff0c; torchaudio版本不一致导致。 方案 这里以pointmlp&am…

Sharding-JDBC分库分表之SpringBoot主从配置

Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 前言 在开发中&#xff0c;如果对数据库的读和写都在一个数据服务器中操作&#xff0c;面对日益增加的访问量&#x…

解锁算力新极限,Xilinx UltraScale+赋能的高性能低延时FPGA加速卡

01、产品概述 AiHPC-V9P 是一款基于 AMD Virtex UltraScale FPGA VU9P 的 PCIe Gen3.0 x16 接口智能网卡&#xff0c;具有最大2*200GbE /或者16*10GbE(典型应用&#xff09;接入容量的高性能低延时智能网卡。 对外接口支持两组QSFP-DD 最高25Gb/s x8Lane 光口接入&#xf…

数据库系统概论 | MySQL | 数据定义 | 单表查询 | 嵌套查询 | 连接查询 | 带有谓词的查询

数据定义 模式的定义与删除 定义模式与删除模式&#xff1a; CREATE SCHEMA S_C_SC; DROP SCHEMA S_C_SC;进入模式&#xff1a; USE S_C_SC;建立学生表&#xff1a; CREATE TABLE Student (Sno CHAR(8) PRIMARY KEY, Sname VARCHAR(20) UNIQUE, Ssex CHAR(6), Sbirthdate …

【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言1、无法链接…

北斗防爆手持终端在化工厂的安全性能分析

北斗防爆手持终端在化工厂中的应用显著提升了安全性能&#xff0c;其卓越的防爆设计、高精度定位与监控功能、实时通信能力以及多功能集成特性&#xff0c;共同构筑了化工厂安全生产的坚实防线&#xff0c;确保了巡检人员与设备在复杂环境下的安全作业与高效管理。 北斗防爆手持…

AE-图层

目录 图层初体验 项目、合成和图层的关系 图层的通用参数 锚点&#xff08;快捷键A&#xff09; 位置&#xff08;快捷键P&#xff09; 缩放&#xff08;快捷键S&#xff09; 旋转&#xff08;快捷键R&#xff09; 不透明度&#xff08;快捷键T&#xff09; 向后平移锚…

实时监测、智能预警:电缆光纤测温系统|原理、应用与前景

实时监测、智能预警&#xff1a;电缆光纤测温系统|原理、应用与前景 电缆光纤测温系统&#xff0c;作为现代电力系统中不可或缺的一部分&#xff0c;以其独特的优势在电缆安全监控领域发挥着日益重要的作用。该系统利用光纤传感技术&#xff0c;实时监测电缆的运行温度&#x…

网站高性能架构设计——高性能缓存架构

从公众号转载&#xff0c;关注微信公众号掌握更多技术动态 --------------------------------------------------------------- 一、缓存基础 1.缓存简介 缓存提升性能的幅度&#xff0c;不只取决于存储介质的速度&#xff0c;还取决于缓存命中率。为了提高命中 率&#xff0c…

【博主推荐】HTML5好看的酷酷的个人简历、个人主页、个人网站源码

文章目录 1.设计来源1.1 主界面1.2 关于我界面1.3 我的项目界面1.4 我的经验界面1.5 我的技能界面1.6 我的文章界面1.7 联系我界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeig…

智能猫砂盆怎么买才不踩雷?2024热门的三款智能猫砂盆分享!

上班外出来不及铲屎怎么办&#xff1f;那当然是入手一个智能猫砂盆啦。实不相瞒&#xff0c;以前我也是被手动铲屎长期折磨的可怜铲屎官&#xff0c;但上班出差哪有空一直盯着猫砂盆看呢&#xff1f;索性后面一不做二不休直接购入了智能猫砂盆。如果你也想将家里的普通猫砂盆换…

如何让 3D 数字孪生场景闪闪发光

今日图扑软件功能分享&#xff1a;我们将探讨 HT 系统如何通过分组管理灯光、裁切体和流光&#xff0c;以提高场景光影效果的精准度和整体可控性。 HT 中的灯光、裁切体、流光是会影响它所在区域一定范围内的其他节点的表现&#xff0c;如 场景中有个 A 灯光&#xff0c;默认情…

阿里云登陆Centos7

用自己电脑登陆Centos7太麻烦了&#xff0c;还要自己弄个虚拟机&#xff0c;一个电脑里面既有WIN又有LINUX&#xff0c;索性直接买个阿里云服务器&#xff0c;来学习Centos7。 购买 我是新用户&#xff0c;可以试用3个月&#xff0c;先用个3个月再说哈哈哈。 一系列操作之后…

电竞玩家的云端盛宴!四大云电脑平台:ToDesk、顺网云、青椒云、极云普惠云实测大比拼

本文目录 一、云电脑概念及市场需求二、云电竞性能测试2.1 ToDesk云电脑2.2 顺网云2.3 青椒云2.4 极云普惠云电脑 三、四大云电脑平台综合配置对比3.1 CPU处理器3.2 GPU显卡3.3 内存 四、总结 一、云电脑概念及市场需求 在数字化时代的推动下&#xff0c;云计算技术日益成熟&a…

关于嵌入式系统中的LED控制程序的一篇爽文

嵌入式系统中的LED控制程序 在嵌入式系统中控制LED是一个很常见的任务&#xff0c;可以用于指示状态、显示信息等。我们将使用C语言编写一个简单的LED控制程序&#xff0c;该程序将控制一个虚拟的LED&#xff0c;但可以根据需要将其扩展到实际的硬件上。 准备工作 在开始之前…