JMH使用指南_jmh 使用-程序员宅基地

技术标签: code  beetl  

关于JMH,可以直接查看官网地址http://openjdk.java.net/projects/code-tools/jmh/

本博客内容来自我正在撰写的新书《Java性能优化(暂定名)》,也欢迎购买经典书《Spring Boot 2 实战权威指南》

1.3 JMH

1.3.1 使用JMH

通过手工编写一个性能压测程序有较多的问题

  • 不同需要性能比较方法放到一个虚拟机里调用,有可能会互相影响。最好的办法是分成俩个独立的进程运行,确保俩个对比方法不相互影响。
  • PerformaceAreaTest启动后直接运行, 缺少预热代过程。虚拟机在执行代码过程中,会加载类,解释执行,以及有可能的优化编译。需要确保虚拟机进行了一定预热运行,以保证测试的公平性,我们在运行PerformaceAreaTest2的时候,能看到第一次循环执行时间总是较长。可以参考第8章了解JIT
  • 为了避免环境影响造成的对结果统计不准,我们需要运行多次,取出平均成绩
  • 需要从多个纬度统计方法的性能,统计冷启动需要消耗的时间,统计OPS,TP99的功能。

JMH使用OPS来表示吞吐量,OPS,Opeartion Per Second,是衡量性能的重要指标,指得是每秒操作量。数值越大,性能越好。类似的概念还有TPS,表示每秒的事务完成量,QPS,每秒的查询量。 如果对每次执行时间进行升序排序,取出总数的99%的最大执行时间作为TP99的值,TP99通常是衡量系统性能重要指标,他表示99%的请求的响应时间不超过某个值。比TP99更严格的事TP999,要求99.9%的请求不超过某个值

有什么工具能帮助我们统计性能优化后的效果,比如更方便的统计OPS,TP99等。同时,我们为了做调优,不必每次都自己写一个测试程序

JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。主要是基于方法层面的基准测试,精度可以达到纳秒级。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析。

JMH 实现了JSR269规范,即注解处理器,能在编译Java源码的时候,识别的到需要处理的注解,如@Beanmark,JMH能根据@Beanmark的配置生成一系列测试辅助类。关于JSR269,本书11章详细介绍. 流行开源Lombok 基于JSR269规范

开始是使用JMH,可以在工程里添加对JMH的依赖,添加如下

<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>

${jmh.version} 为jmh最新版本,为1.0

我们编写一个JMH测试类

@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(1)
@Fork(1)
@OutputTimeUnit(TimeUnit.SECONDS)
public class MyBenchmark {
   	@Benchmark
    public static void  testStringKey(){
        //优化前的代码
    }
    @Benchmark
    public static void  testObjectKey(){
       //要测试的优化后代码
    }
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(MyBenchmark.class.getSimpleName())
                .build();
        new Runner(opt).run();
    }
}

MyBenchmark 有俩个需要比较的方法,都用 @Benchmark注解标识,MyBenchmark用了一系列注解,解释如下

  • BenchmarkMode,使用模式,默认是Mode.Throughput,表示吞吐量,其他参数还有AverageTime,表示每次执行时间,SampleTime表示采样时间,SingleShotTime表示只运行一次,用于测试冷启动消耗时间,All表示统计前面的所有指标
  • Warmup 配置预热次数,默认是每次运行1秒,运行10次,我们的例子是运行3次
  • Measurement 配置执行次数,本例是一次运行5秒,总共运行3次。在性能对比时候,采用默认1秒即可,如果我们用jvisualvm做性能监控,我们可以指定一个较长时间运行。
  • Threads 配置同时起多少个线程执行,默认值世 Runtime.getRuntime().availableProcessors(),本例启动1个线程同时执行
  • Fork,代表启动多个单独的进程分别测试每个方法,我们这里指定为每个方法启动一个进程。
  • OutputTimeUnit 统计结果的时间单元,这个例子TimeUnit.SECONDS,我们在运行后会看到输出结果是统计每秒的吞吐量

我们在MyBenchmark添加需要的测试方法,如下

static AreaService areaService = new AreaService();
static PreferAreaService perferAreaService = new PreferAreaService();
static List<Area> data = buildData(20);

@Benchmark
public static void  testStringKey(){
    areaService.buildArea(data);
}
@Benchmark
public static void  testObjectKey(){
    perferAreaService.buildArea(data);
}

private static List<Area> buildData(int count){
    List<Area>  list = new ArrayList<>(count);
    for(int i=0;i<count;i++){
        Area area = new Area(i,i*10);
        list.add(area);
    }
    return list;
}

因为MyBenchmark包含了一个main方法,我们可以直接在IDE里直接运行这个方法,有如下输出

# Warmup: 3 iterations, 1 s each
# Measurement: 3 iterations, 5 s each
# Threads: 1 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time

以上输出来自于我们的配置,第一行表示预热3次,每次执行1秒,第二行表示运行3次,每次运行5秒,这部分的运行结果计入统计。第三行表示1个线程执行,第四行统计性能数据纬度是Throughput,吞吐量

紧接着会运行testObjectKey方法,有如下输出

# Benchmark: com.ibeetl.code.ch01.test.MyBenchmark.testObjectKey

# Run progress: 0.00% complete, ETA 00:00:36
# Fork: 1 of 1
objc[68658]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.
# Warmup Iteration   1: 1288302.671 ops/s
# Warmup Iteration   2: 3061587.202 ops/s
# Warmup Iteration   3: 1094970.828 ops/s
Iteration   1: 2491836.097 ops/s
Iteration   2: 2780362.118 ops/s
Iteration   3: 3621313.883 ops/s

这里的Fork表示子进程,我们只配置里一个,因此只有一个进程的执行结果,该进程包含预热3次,每次1秒,以及运行3次,每次运行5秒,执行完testObjectKey方法后,会自动打印一个汇总信息

Result: 939996.216 ±(99.9%) 2012646.237 ops/s [Average]
  Statistics: (min, avg, max) = (813154.364, 939996.216, 1013607.616), stdev = 110319.932
  Confidence interval (99.9%): [-1072650.021, 2952642.453]

统计结果给出了多次测试后的最小值,最大值和均值,以及标准差 (stdev),置信区间(Confidence interval)

标准差(stdev)反映了数值相对于平均值得离散程度,置信区间是指由样本统计量所构造的总体参数的估计区间。在统计学中,一个概率样本的置信区间(Confidence interval)是对这个样本的某个总体参数的区间估计

testStringKey的输出与上面类似,这俩个比较方法执行完毕,会自动打印出一个性能对比数据表格

Benchmark                               Mode  Samples        Score  Score error  Units
c.i.c.c.t.MyBenchmark.testObjectKey    thrpt        3  1976766.072   408421.217  ops/s
c.i.c.c.t.MyBenchmark.testStringKey    thrpt        3   423788.869   222139.136  ops/s

Benchmark列表示这次测试对比的方法,Mode列表上结果的统计纬度,Samples列表示采样次数,Samples=Fork*Iteration。Score是对这次评测的打分,对于testObjectKey,意味着他的OPS为每秒1976766,大约4倍testStringKey方法

Score Error 这里表示性能统计上的误差,我们不需要关心这个数据,主要查看Score

可以修改统计纬度,比如修改为Mode.SampleTime,时间按照纳秒统计


@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
......
public class MyBenchmark {}

可以看到有一组如下统计

p( 0.0000) =   1992.000 ns/op
p(50.0000) =   2084.000 ns/op
p(90.0000) =   2464.000 ns/op
p(95.0000) =   3472.000 ns/op
p(99.0000) =   4272.000 ns/op
p(99.9000) =  17481.920 ns/op
p(99.9900) =  80659.840 ns/op
p(99.9990) = 562593.690 ns/op
p(99.9999) = 745472.000 ns/op

可以看到90%的调用,是在2464纳秒内完成,99%的调用都是在4272纳秒完成的.

1.3.2 JMH常用设置

在这个例子,我们性能测试所依赖的对象areaService,perferAreaService 恰好是线程安全的,大多数时候性能测试方法都会引用一些外部实例对象,考虑到多线程测试访问这些实例对象,JMH要求必须为这些变量申明是Thread 内生效,还是整个BeanMark使用。如果是前者,JMH会为每个线程构建一个新的实例,后者则所有测试都共享这个变量,JMH用@State注解来说明对象的生命周期,@State注解作用在类上,比如,在MyBenchmark例子里,我们可以改成如下例子

@State(Scope.Benchmark)
public static class SharedPara{
    AreaService areaService = new AreaService();
    PreferAreaService perferAreaService = new PreferAreaService();
    List<Area> data = buildData(20);

    private  List<Area> buildData(int count){
        //忽略其他代码
    }

}

@Benchmark
public  void  testStringKey(SharedPara para){
    para.areaService.buildArea(para.data);
}
@Benchmark
public  void  testObjectKey(SharedPara para){
    para.perferAreaService.buildArea(para.data);
}

必须申明一公共静态内部类,该类包含了我们需要使用的实例对象,并在该类用@State注解表明这个对象是Thread的还是BeanchMark范围内使用。在这个例子里,因为配置为Scope.Benchmark,JMH在整个性能测试过程中,只构造一个SharedPara实例,SharedPara 作为参数传入每个待测试的方法。

也可以不使用内部类,直接使用申明性能测试的类,在类上使用@State注解

@State(Scope.Benchmark)
public class MyBenchmarkStateSimple {
  AreaService areaService = new AreaService();
  PreferAreaService perferAreaService = new PreferAreaService();
  List<Area> data = buildData(20);
  //忽略其他代码
}

@Setup 和 @TearDown 是一对注解,作用于方法上,前者用于测试前的初始化工作,后者用于回收某些资源,比如压测前需要准备一些数据

@State(Scope.Benchmark)
public class ScriptEngineBeanchmrk {
    String script = null;
    @Benchmark
    public void nashornTest(){
		// ... 测试方法
    }
    
    @Setup
    public void loadScriptFromFile(){
		//加载一个测试脚本
    }

}

@Level 用于控制 @Setup,@TearDown 的调用时机,有如下含义

  • Level.Tiral: 运行每个性能测试的时候执行,推荐的方式。
  • Level.Iteration, 每次迭代的时候执行
  • Level.Invocation,每次调用方法的时候执行,这个选项需要谨慎使用。

JMH提供了Runner类能运行Benchmark类

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
        .include(MyBenchmark.class.getSimpleName())
        .build();
    new Runner(opt).run();
}

include接受一个字符串表达式,表示需要测试的类和方法,如上例子测试所有方法MyBenchmark。如下例子则只测试方法名字包含“testObjectKey“的方法

include(MyBenchmark.class.getSimpleName()+".*testObjectKey*")

OptionsBuilder包含了多个方法用于配置性能测试,可以指定循环次数,预热次数等,如下例子会用4个子进程做性能测试,每个进程预热一次,执行5次迭代

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
        .include(MyBenchmark.class.getSimpleName())
        .forks(4)
        .warmupIterations(1)
        .measurementIterations(5)
        .build();
    new Runner(opt).run();
}

截至到目前为止,JMH都是通过一个main方法在IDE里执行,更为通常情况,JMH推荐使用单独的一个Maven工程来执行性能测试而不要放到业务工程里。可以通过maven archetype:generate 命令来生成一个心得JMH Maven工程。

mvn archetype:generate
          -DinteractiveMode=false
          -DarchetypeGroupId=org.openjdk.jmh
          -DarchetypeArtifactId=jmh-java-benchmark-archetype
          -DgroupId=code.ibeetl.com
          -DartifactId=first-benchmark
          -Dversion=1.0

为了阅读方便,分成几行,如上命令行应该放到一行执行,执行完毕后,生成了一个maven工程,maven工程仅仅包含了一个 MyBenchmark 例子。

package org.sample;

import org.openjdk.jmh.annotations.Benchmark;

public class MyBenchmark {

    @Benchmark
    public void testMethod() {
        // place your benchmarked code here
    }
}

我们可以修改MyBenchmark,添加我们需要测试的代码, 现在,可以创建一个性能测试的jar文件,通过运行如下maven命令

mvn clean install

命令会在target目录下生成一个benchmarks.jar,包含了运行性能测试所需的任何东西,在命令行运行如下命令

java -jar target/benchmarks.jar  MyBenchmark

JMH将会被启动,默认情况下运行MyBenchmark类里的所有被@Benchmark标注方法

有些性能测试需要了解不同输入参数的性能,比如对于模板引擎的性能测试中,考虑到字节流输出和字符流输出


@Param({"1","2","3"})
int  outputType;
@Benchmark
public String benchmark() throws TemplateException, IOException {
  if(outputType==3){
			return doStream();
  }else if(outputType==2) {
    return doCharStream()
  }else{
    return  doString();
  }
 
}

JMH会分别赋值outpuType为1,2,3后,在各自测试一次,会输出如下

Benchmark	      (outputType)	Score	    Units
Beetl.benchmark	    1	        44977.421	ops/s
Beetl.benchmark	    2	        34931.724	ops/s
Beetl.benchmark	    3	        59175.106	ops/s

1.3.3 注意事项

编写JHM代码,需要考虑到虚拟机的优化,而使得测试失真,如下measureWrong代码就是所谓的Dead-Code代码

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JMHSample_08_DeadCode {
  private double x = Math.PI;

  @Benchmark
  public void baseline() {
    //基准
  }

  @Benchmark
  public void measureWrong() {
    //虚拟机会优化掉这部分,性能同baseline
    Math.log(x);
  }

  @Benchmark
  public double measureRight() {
    // 真正的性能测试
    return Math.log(x);
  }
}

测试结果如下

Benchmark                                               Mode     Score    Units    
c.i.c.c.c.i.c.c.j.JMHSample_08_DeadCode.baseline        avgt     0.358    ns/op    
c.i.c.c.c.i.c.c.j.JMHSample_08_DeadCode.measureRight    avgt    24.605    ns/op    
c.i.c.c.c.i.c.c.j.JMHSample_08_DeadCode.measureWrong    avgt     0.366    ns/op    

在测试measureWrong方法,JIT能推测出方法体可以被优化调而不影响系统,measureRight因为定义了返回值,JIT不会优化。

下一个是关于常量折叠,JIT认为方法计算结果为常量,从而优化直接返回常量给调用者

 private double x = Math.PI;
 private final double wrongX = Math.PI;

  @Benchmark
  public double baseline() {
    // 基准测试
    return Math.PI;
  }

  @Benchmark
  public double measureWrong_1() {
    // JIT认为是个常量
    return Math.log(Math.PI);
  }

  @Benchmark
  public double measureWrong_2() {
    // JIT认为方法调用结果是个常量.
    return Math.log(wrongX);
  }

  @Benchmark
  public double measureRight() {
    // 正确的测试
    return Math.log(x);
  }

如下是测试结果

Benchmark                                                     Mode    Score   Units           
c.i.c.c.c.i.c.c.j.JMHSample_10_ConstantFold.baseline          avgt    1.175   ns/op           
c.i.c.c.c.i.c.c.j.JMHSample_10_ConstantFold.measureRight      avgt   25.805   ns/op           
c.i.c.c.c.i.c.c.j.JMHSample_10_ConstantFold.measureWrong_1    avgt    1.116   ns/op           
c.i.c.c.c.i.c.c.j.JMHSample_10_ConstantFold.measureWrong_2    avgt    1.031   ns/op           

考虑到inline对性能影响很大,JMH支持 @CompilerControl来控制是否允许内联

public class Inline {
  int x=0,y=0;
  @Benchmark
  @CompilerControl(CompilerControl.Mode.DONT_INLINE)
  public  int   add(){
    return dataAdd(x,y);
  }

  @Benchmark
  public  int  addInline(){
    return dataAdd(x,y);
  }

  private int  dataAdd(int x,int y){
    return x+y;
  }
  @Setup
  public void init() {
    x = 1;
    y = 2;
  }
}

add和addInline方法都会调用dataAdd方法,前者使用CompilerControl类,可以用在方法或者类上,来提供编译选项

  • DONT_INLINE,调用方法不内联
  • INLINE,调用方法内联
  • BREAK,插入一个调试断点(TODO,如何调试,参考11章)
  • PRINT,打印方法被JIT编译后的机器码信息

开发人员可能觉得上面的测试,add方法太简单,会习惯性的在add方法里方一个循环,以减少JMH调用add方法的成本。JMH不建议这么做,因为JIT会实际上对这种循环会做优化,以消除循环调用成本。如下是个例子可以看到循环测试结果不准确

int x = 1;
int y = 2;

/** 正确测试
*/
@Benchmark
public int measureRight() {
  return (x + y);
}


private int reps(int reps) {
  int s = 0;
  for (int i = 0; i < reps; i++) {
    s += (x + y);
  }
  return s;
}

@Benchmark
@OperationsPerInvocation(1)
public int measureWrong_1() {
  return reps(1);
}

@Benchmark
@OperationsPerInvocation(10)
public int measureWrong_10() {
  return reps(10);
}

@Benchmark
@OperationsPerInvocation(100)
public int measureWrong_100() {
  return reps(100);
}

@Benchmark
@OperationsPerInvocation(1000)
public int measureWrong_1000() {
  return reps(1000);
}

注解OperationsPerInvocation 告诉JMH统计性能的时候需要做修正,比如@OperationsPerInvocation(10)调用了10次。

性能测试结果如下

编写性能测试的一个好习惯是先编写一个单元测试用例,以确保性能测试准确性,x  Benchmark                                                   Mode   Score   Units    c.i.c.c.c.i.c.c.j.JMHSample_11_Loops.measureRight           avgt   1.114   ns/op    c.i.c.c.c.i.c.c.j.JMHSample_11_oops.measureWrong_1         avgt   1.057   ns/op    c.i.c.c.c.i.c.c.j.JMHSample_11_Loops.measureWrong_10        avgt   0.139   ns/op    c.i.c.c.c.i.c.c.j.JMHSample_11_Loops.measureWrong_100       avgt   0.018   ns/op    c.i.c.c.c.i.c.c.j.JMHSample_11_Loops.measureWrong_1000      avgt   0.035   ns/op    java

可以看到,测试方法里使用循环,会促使JIT进行优化,做循环消除(参考第8章JIT TODO)

1.3.4 单元测试

无论是编写JMH,或者其他性能测试程序,好习惯是先编写一个单元测试用例,以确保性能测试方法的准确性,对于1.3.4的Inline类,可以先编写一个单元测试用例,确保add和addInline返回正确结果

public class InLineTestJunit {
  @Test
  public void test(){
    Inline inline = new Inline();
    inline.init();
    //期望结果
    int expectd = inline.x+inline.y;
    int ret = inline.add();
    int ret2 = inline.addInline();
    Assert.assertEquals(expectd,ret);
    Assert.assertEquals(expectd,ret2);
  }
}

在JMH工程调用maven install 生成测试代码的时候,会进行单元测试,从而保证测试结果的准确

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiandafu/article/details/94029094

智能推荐

安卓工具类:FileUtils_fileutils 注入-程序员宅基地

文章浏览阅读268次。public class MineFileUtils { /** * 使用本地安装的文件查询器,打卡文件 * * @param activity * @param filepath */ public static void openAndroidFile(Activity activity, String filepath) ..._fileutils 注入

如果有人能力不如你工资比你高怎么看?_比你菜工资比你高-程序员宅基地

文章浏览阅读5.1k次,点赞3次,收藏3次。你在公司里工作,如果同办公室里的一个人,能力没有你强,但工资却高于你,你会不会有想法,心理能平衡吗?1. 错误回答:1) 我当然不平衡,那我还干的什么意思?2) 如果他的能力比我强,我不会有想法。如果没有我强,我肯定心理不平衡。(路健就是这样回答的)3) 如果公司对待员工是这样的不公平,肯定企业文化有问题,这样的公司只有走人。2. 正确回答:(别忘了换位思考原则)工资是员工最敏感的问题,公司一般都会尽量处理好,如果那个同事的能力不如我,工资还高于我,肯定是他在其他方面强于我。或者,他能为公司解决_比你菜工资比你高

获取对话框当前cfont_VC调用系统字体对话框-程序员宅基地

文章浏览阅读474次。1、通过MFC类调用字体对话框2、通过win32API函数调用字体对话框通过MFC类调用字体对话框CFontDialog构造函数CFontDialog(LPLOGFONTlplfInitial=NULL,DWORDdwFlags=CF_EFFECTS|CF_SCREENFONTS,CDC*pdcPrinter=NULL,CWnd*pParentWnd=NULL..._vc++如何获取当前计算中的系统字体

《树莓派Python编程入门与实战》——3.2 检查你的Python环境-程序员宅基地

文章浏览阅读304次。本节书摘来异步社区《树莓派Python编程入门与实战》一书中的第3章,第3.2节,作者:【美】Richard Blum,更多章节内容可以访问云栖社区“异步社区”公众号查看3.2 检查你的Python环境树莓派Python编程入门与实战Raspbian发行版默认安装了Python第三版环境和一些必要的工具。下面是预装了的Python功能。Python..._怎么查树莓派是否有python环境?

工具IDEA 配置springboot+maven项目-程序员宅基地

文章浏览阅读81次。工具IDEA 配置springboot+maven项目 首先安装IDEA,至于怎么安装就不介绍了。。第一步 配置maven环境 首先安装maven,先在网上下载一个maven包。在IDEA的settings中Maven设置 点击USer settings file 文件夹正常的是空白 如图找到你下载的maven文件夹,引入setti..._maven项目怎么配置springboot

GSL 系列 5 — 向量和矩阵 2 — 向量 (vector)_gsl_vector *-程序员宅基地

文章浏览阅读1.3k次。文章目录0 写在前面1 向量 (vector)0 写在前面因为向量是构建于块之上,请先理解块,参见:GSL 系列 5 — 向量和矩阵 1 — 块 (block)1 向量 (vector)向量建构于块之上,添加了对块的切片描述,向量切片必须是内存空间中一组等间隔的元素,不同的向量可以创建于一个块之上,定义如下:// gsl_vector_double.htypedef struct {..._gsl_vector *

随便推点

soapui 自动化教程(三)_soapui nosr换行显示-程序员宅基地

文章浏览阅读4.6k次,点赞6次,收藏9次。soapui 之 groovy 进阶上一节讲到如何使用groovy脚本执行用例。def testStep = TEST_SUITE.getTestCaseByName('TestSuite').getTestStepByName('login')def testStepContext = new WsdlTestRunContext(testStep)def result = testStep._soapui nosr换行显示

python之argparse模块_argparse 参数缩写-程序员宅基地

文章浏览阅读2.6k次。argparse 模块使编写用户友好的命令行界面变得更容易.程序只需定义好它要求的参数,然后argparse将负责如何从sys.argv中解析出这些参数。argparse模块还会自动生成帮助和使用信息并且当用户赋给程序非法的参数时产生错误信息。1.ArgumentParser类class argparse.ArgumentParser(prog=None, usage=None, de_argparse 参数缩写

浅析LiveMedia智能视频网关的AI识别技术及应用场景_.net ai 识别视频直播的话语-程序员宅基地

文章浏览阅读239次。LiveMedia智能视频边缘网关,支持对多路网络IPC和NVR设备的高清视频流进行实时智能分析。通过将网络摄像机、NVR、编码器等视频源设备统一集中接入和汇聚管理,对接入的多路高清视频流进行人、车、物、行为等实时检测与分析,结合硬件中内置的多种AI算法,实现人脸识别/检测、车辆识别/检测、目标检测、行为识别等目的,可对异常行为事件进行告警与提醒,支持对接视频结构化数据平台、大数据综合分析平台等。_.net ai 识别视频直播的话语

Kinetis KL8x 使用eDMA模块接收串口数据_site: csdn.net edma循环-程序员宅基地

文章浏览阅读1.6k次。飞思卡尔的芯片KL系列Cortex-M0+内核的,其他的应该可以通用,大体一致,之前在KL25上用过,这次是KL81,我对比两者使用类似,就是某些寄存器不同罢了正文开始:需要用LPUART接收上层接口的数据,比较大,而且大小不固定,之前用FIFO来接收,但是遇到收发错乱,很不稳定,故使用eDMA来接收#include "fsl_port_hal.h"#include "fsl_dev_site: csdn.net edma循环

OpenSSL杂记(CA证书)-程序员宅基地

文章浏览阅读896次。OpenSSL和OpenSSHOpenSSH只允许白名单的用户登录1、限制前:[email protected]'s password: [ww@qq ~]$ exitlogoutConnection to 10.201.106.129 closed.[root@zz ~]# ssh [email protected]@10.201.106.129's password: ...

GPIO 口的输入,输出模式及其说明_gpio_mode_in-程序员宅基地

文章浏览阅读4.2w次,点赞65次,收藏351次。GPIO端口各种模式的区别(1)GPIO_Mode_AIN 模拟输入(2)GPIO_Mode_IN_FLOATING 浮空输入(3)GPIO_Mode_IPD 下拉输入(4)GPIO_Mode_IPU 上拉输入(5)GPIO_Mode_Out_OD 开漏输出(6)GPIO_Mode_Out_PP 推挽输出(7)GPIO_Mode_AF_OD 复用开漏输出(8)GPIO_Mode_A..._gpio_mode_in