• [交流吐槽] JVM原理学习总结
    这篇总结主要是基于我之前JVM系列文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢#更多详细内容可以查看我的专栏文章:深入理解JVM虚拟机https://blog.csdn.net/column/details/21960.htmlJVM介绍和源码首先JVM是一个虚拟机,当你安装了jre,它就包含了jvm环境。JVM有自己的内存结构,字节码执行引擎,因此class字节码才能在jvm上运行,除了Java以外,Scala,groovy等语言也可以编译成字节码而后在jvm中运行。JVM是用c开发的。JVM内存模型内存模型老生常谈了,主要就是线程共享的堆区,方法区,本地方法栈。还有线程私有的虚拟机栈和程序计数器。堆区存放所有对象,每个对象有一个地址,Java类jvm初始化时加载到方法区,而后会在堆区中生成一个Class对象,来负责这个类所有实例的实例化。栈区存放的是栈帧结构,栈帧是一段内存空间,包括参数列表,返回地址,局部变量表等,局部变量表由一堆slot组成,slot的大小固定,根据变量的数据类型决定需要用到几个slot。方法区存放类的元数据,将原来的字面量转换成引用,当然,方法区也提供常量池,常量池存放-128到127的数字类型的包装类。字符串常量池则会存放使用intern的字符串变量。JVM OOM和内存泄漏这里指的是oom和内存泄漏这类错误。oom一般分为三种,堆区内存溢出,栈区内存溢出以及方法区内存溢出。堆内存溢出主要原因是创建了太多对象,比如一个集合类死循环添加一个数,此时设置jvm参数使堆内存最大值为10m,一会就会报oom异常。栈内存溢出主要与栈空间和线程有关,因为栈是线程私有的,如果创建太多线程,内存值超过栈空间上限,也会报oom。方法区内存溢出主要是由于动态加载类的数量太多,或者是不断创建一个动态代理,用不了多久方法区内存也会溢出,会报oom,这里在1.7之前会报permgem oom,1.8则会报meta space oom,这是因为1.8中删除了堆中的永久代,转而使用元数据区。内存泄漏一般是因为对象被引用无法回收,比如一个集合中存着很多对象,可能你在外部代码把对象的引用置空了,但是由于对象还被集合给引用着,所以无法被回收,导致内存泄漏。测试也很简单,就在集合里添加对象,添加完以后把引用置空,循环操作,一会就会出现oom异常,原因是内存泄漏太多了,导致没有空间分配新的对象。常见调试工具命令行工具有jstack jstat jmap 等,jstack可以跟踪线程的调用堆栈,以便追踪错误原因。jstat可以检查jvm的内存使用情况,gc情况以及线程状态等。jmap用于把堆栈快照转储到文件系统,然后可以用其他工具去排查。visualvm是一款很不错的gui调试工具,可以远程登录主机以便访问其jvm的状态并进行监控。class文件结构class文件结构比较复杂,首先jvm定义了一个class文件的规则,并且让jvm按照这个规则去验证与读取。开头是一串魔数,然后接下来会有各种不同长度的数据,通过class的规则去读取这些数据,jvm就可以识别其内容,最后将其加载到方法区。JVM的类加载机制jvm的类加载顺序是bootstrap类加载器,extclassloader加载器,最后是appclassloader用户加载器,分别加载的是jdk/bin ,jdk/ext以及用户定义的类目录下的类(一般通过ide指定),一般核心类都由bootstrap和ext加载器来加载,appclassloader用于加载自己写的类。双亲委派模型,加载一个类时,首先获取当前类加载器,先找到最高层的类加载器bootstrap让他尝试加载,他如果加载不了再让ext加载器去加载,如果他也加载不了再让appclassloader去加载。这样的话,确保一个类型只会被加载一次,并且以高层类加载器为准,防止某些类与核心类重复,产生错误。defineclass findclass和loadclass类加载classloader中有两个方法loadclass和findclass,loadclass遵从双亲委派模型,先调用父类加载的loadclass,如果父类和自己都无法加载该类,则会去调用findclass方法,而findclass默认实现为空,如果要自定义类加载方式,则可以重写findclass方法。常见使用defineclass的情况是从网络或者文件读取字节码,然后通过defineclass将其定义成一个类,并且返回一个Class对象,说明此时类已经加载到方法区了。当然1.8以前实现方法区的是永久代,1.8以后则是元空间了。JVM虚拟机字节码执行引擎jvm通过字节码执行引擎来执行class代码,他是一个栈式执行引擎。这部分内容比较高深,在这里就不献丑了。编译期优化和运行期优化编译期优化主要有几种1 泛型的擦除,使得泛型在编译时变成了实际类型,也叫伪泛型。2 自动拆箱装箱,foreach循环自动变成迭代器实现的for循环。3 条件编译,比如if(true)直接可得。运行期优化主要有几种1 JIT即时编译Java既是编译语言也是解释语言,因为需要编译代码生成字节码,而后通过解释器解释执行。但是,有些代码由于经常被使用而成为热点代码,每次都编译太过费时费力,干脆直接把他编译成本地代码,这种方式叫做JIT即时编译处理,所以这部分代码可以直接在本地运行而不需要通过jvm的执行引擎。2 公共表达式擦除,就是一个式子在后面如果没有被修改,在后面调用时就会被直接替换成数值。3 数组边界擦除,方法内联,比较偏,意义不大。4 逃逸分析,用于分析一个对象的作用范围,如果只局限在方法中被访问,则说明不会逃逸出方法,这样的话他就是线程安全的,不需要进行并发加锁。1JVM的垃圾回收1 GC算法:停止复制,存活对象少时适用,缺点是需要两倍空间。标记清除,存活对象多时适用,但是容易产生随便。标记整理,存活对象少时适用,需要移动对象较多。2 GC分区,一般GC发生在堆区,堆区可分为年轻代,老年代,以前有永久代,现在没有了。年轻代分为eden和survior,新对象分配在eden,当年轻代满时触发minor gc,存活对象移至survivor区,然后两个区互换,等待下一场gc,当对象存活的阈值达到设定值时进入老年代,大对象也会直接进入老年代。老年代空间较大,当老年代空间不足以存放年轻代过来的对象时,开始进行full gc。同时整理年轻代和老年代。一般年轻代使用停止复制,老年代使用标记清除。3 垃圾收集器serial串行parallel并行它们都有年轻代与老年代的不同实现。然后是scanvage收集器,注重吞吐量,可以自己设置,不过不注重延迟。cms垃圾收集器,注重延迟的缩短和控制,并且收集线程和系统线程可以并发。cms收集步骤主要是,初次标记gc root,然后停顿进行并发标记,而后处理改变后的标记,最后停顿进行并发清除。g1收集器和cms的收集方式类似,但是g1将堆内存划分成了大小相同的小块区域,并且将垃圾集中到一个区域,存活对象集中到另一个区域,然后进行收集,防止产生碎片,同时使分配方式更灵活,它还支持根据对象变化预测停顿时间,从而更好地帮用户解决延迟等问题。
  • [问题求助] 【Atlas200DK】【多线程】使用acl.util.start_thread开启线程,debug无法跳入
    【功能模块】acl库【操作步骤&问题现象】使用acl.util.start_thread开启的线程,在使用VScode进行远程开发时,在debug的时候打断点,无法跳入开启的线程。而使用原生的多线程函数则可以跳入。
  • [性能劣化] 外表导入40G数据耗时1小时
    外表导入40G数据耗时1小时,性能裂化严重语句执行慢,打印执行计划发现慢在stream上。使用gsar发现业务卡持续有重传。netstat -anop|grep “on (”|grep -v “/0/0”|sort -rnk3|head发现确实有超过一次的重传。均衡网卡绑核,没有效果,重传仍比较严重。测试ping时延和重传率有较大关联。<<get_irq_affinity2.sh>> <<set_irq_affinity2.sh>>查看重传对端节点top,ksoftirq仍很高。BIOS上SMMU确认是关闭的,重启过服务器的节点ksoftirq低,未重启的ksoftirq高。cat /proc/softirqs | awk '{print $1,$10,$11}' ksoftirq计数增长较慢,但CPU占用高。while true;do date;cat /proc/112/stack;echo;done > stack.log 抓取CPU高的ksoftirq进程堆栈,栈都在switch_to函数,未见异常。perf record -p 进程号 -ag sleep 10perf report抓CPU高的ksoftirq进程的热点函数,发现packet_rcv和run_filter函数异常,run_filter由packet_rcv调用,一般packet_rcv不是tcp连接用到的函数cat /proc/net/ptype 没有packet_rcv函数显示tcpdump !tcp -i nic0 抓包未发现异常cat /proc/net/packet发现有四千多结果,通过结果iNode查询lsof -P -n | grep inode 发现是dhclient进程。收包时,每个包都要经过所有的dhclient进程,过多的dhclient进程导致软中断数不多而CPU使用率高。排查定时任务,找到是Ruby用户执行的netcardMonitor会导致dhclient持续增长。每次service network restart都会失败并增加一个dhclient进程,注释掉重启网络服务规避。 
  • [运维技巧] idle线程未清理
    使用clean connection to all for database dbname;清理后仍可以看到idle连接。解决方法:clean connection to all force for database dbname;执行后idle线程全部清理
  • [问题求助] 【MDC300产品】【惯导功能】570D惯导抽象是走UART还是CAN
    【功能模块】【操作步骤&问题现象】1、导远570D惯导通过UART连接到MDC300,并启动ins惯导数据抽象进程2、ins惯导数据抽象没有发出数据3、用mdc-tool监控UART确定MCU是有收到串口数据并转发给HOST的4、用MCD工具查看发现InsCmHostProcess进程通过someip接收的是CAN数据而非UART数据问题:ins惯导数据抽象到底是接收CAN还是UART?【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [流程编排] adc脚本的线程安全问题
    环境:adc2.2问题:多个用户同时访问服务,即使加锁是否存在污染_message的可能性?如用户A发送请求,执行到17行之前,用户B发送请求,污染_message,那么实际上17行的请求是用户B的请求,而用户B执行到12行返回对应的响应码,认为用户B抢占失败。我该如何正确加锁,以确保线程安全?
  • [执行问题] 迭代数据集时进程自动被kill
    【功能模块】MindSpore 1.5.0Ascend 910【操作步骤&问题现象】1、代码运行到此处时,没有任何错误报错或提示,进程被kill,显示killed。dataset['train']的类型为<class 'mindspore.dataset.engine.datasets.GeneratorDataset'>【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] 【Atlas200DK】【多线程同步推理】aclmdlExecute推理报错107004
    【功能描述】1、这是个多人情绪识别项目,第一个模型为retinaface检测多个人脸,然后根据第一个模型推理得出的人脸个数启用多线程来进行第二个模型推理,前面已经成功测试过第二个模型串行执行,但是现在想用多线程来提高整体推理速度;2、第二个情绪模型推理线程函数已经建立了多个context容器来进行并行推理,线程函数如下图2所示。【操作步骤&问题现象】1、第二个情绪模型(在线程函数中)运行到模型推理时报错如下图一所示;2、我找了aclError相关的返回值定义,并没有发现这个107004错误的含义,请大佬帮我看一下这是啥原因。【截图信息】图一:图二:【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] CMS安装完成后,SUM进程启动失败
    【问题来源】星网【功能模块】AICC8.15【操作步骤&问题现象】安装完成CC-CMS后,CMSAPP和SIA进程启动正常,但是SUM启动失败,catalina.out日志截图如下【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 关于杀死Chrome进程后的“恢复页面弹窗”的永久关闭
    相信大家都有遇见,每当我们杀死Chrome进程后,再次打开Chrome,就会在右上角有一个恢复页面的弹窗,很烦人,有时候还会影响到流程的进行。不用担心,接下来就告诉大家如何永久关闭这个“恢复”弹窗。1.首先我们找到自己磁盘中的appdata目录(一般这个都在C盘User目录下,觉得不太好找的可以试试在路径上直接输入%appdata%,回车,再返回这个目录的上一级就可以了)2.然后我们找到后面的地址  C:%users\+\AppData\Local\Google\Chrome%user Data\Default 在这个目录下找到Preferences文件3.我们需要右键选择用记事本打开Preferences文件,在里面直接快捷键CTRL+F 去查找exit_type,将"exit_type":"crashed" 改为"Normal"4.然后我们只需要保存关闭Preferences文件,再右键打开Preferences文件属性,将只读这个选项勾上,保存。即可享受永不弹出“恢复页面”的Chrome浏览器了!
  • [新手课堂] 垃圾收集器 Garbage Collector
    Serial 收集器:一个采用单个线程并基于复制算法工作在新生代的收集器,进行垃圾收集时,必须暂停其它所有的工作线程(Stop The World),是 Client 模式下 JVM 的默认选项,-XX:+UseSerialGCSerial Old 收集器:一个采用单线程基于标记-整理算法并工作在老年代的收集器ParNew 收集器:Serial 收集器的多线程版本(使用多个线程进行垃圾收集),-XX:+UseParNewGCCMS 收集器(Concurrent Mark Sweep):一种以尽量减少停顿时间为目标的收集器,工作在老年代,基于标记-清除算法实现,-XX:+UseConcMarkSweepGCParallel Scavenge 收集器:一个采用多线程基于复制算法并工作在新生代的收集器,也被称作是吞吐量优先的 GC,是早期 jdk1.8 等版本中 Server 模式 JVM 的默认 GC 选择,-XX:+UseParallelGC,使用 Parallel Scavenge(年轻代)+ Serial Old(老年代)的组合进行 GCParallel Old 收集器:一个采用多线程基于标记-整理算法并工作在老年代的收集器,适用于注重于吞吐量及 CPU 资源敏感的场合,-XX:+UseParallelOldGC,使用 Parallel Scavenge(年轻代)+ Parallel Old(老年代)的组合进行 GCG1 收集器:jdk1.7 提供的一个工作在新生代和老年代的收集器,基于标记-整理算法实现,在收集结束后可以避免内存碎片问题,一种兼顾吞吐量和停顿时间的 GC,是 Oracle jdk1.9 以后的默认 GC 选项
  • [交流吐槽] JVM原理学习总结
    这篇总结主要是基于我之前JVM系列文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢#更多详细内容可以查看我的专栏文章:深入理解JVM虚拟机https://blog.csdn.net/column/details/21960.htmlJVM介绍和源码首先JVM是一个虚拟机,当你安装了jre,它就包含了jvm环境。JVM有自己的内存结构,字节码执行引擎,因此class字节码才能在jvm上运行,除了Java以外,Scala,groovy等语言也可以编译成字节码而后在jvm中运行。JVM是用c开发的。JVM内存模型内存模型老生常谈了,主要就是线程共享的堆区,方法区,本地方法栈。还有线程私有的虚拟机栈和程序计数器。堆区存放所有对象,每个对象有一个地址,Java类jvm初始化时加载到方法区,而后会在堆区中生成一个Class对象,来负责这个类所有实例的实例化。栈区存放的是栈帧结构,栈帧是一段内存空间,包括参数列表,返回地址,局部变量表等,局部变量表由一堆slot组成,slot的大小固定,根据变量的数据类型决定需要用到几个slot。方法区存放类的元数据,将原来的字面量转换成引用,当然,方法区也提供常量池,常量池存放-128到127的数字类型的包装类。字符串常量池则会存放使用intern的字符串变量。JVM OOM和内存泄漏这里指的是oom和内存泄漏这类错误。oom一般分为三种,堆区内存溢出,栈区内存溢出以及方法区内存溢出。堆内存溢出主要原因是创建了太多对象,比如一个集合类死循环添加一个数,此时设置jvm参数使堆内存最大值为10m,一会就会报oom异常。栈内存溢出主要与栈空间和线程有关,因为栈是线程私有的,如果创建太多线程,内存值超过栈空间上限,也会报oom。方法区内存溢出主要是由于动态加载类的数量太多,或者是不断创建一个动态代理,用不了多久方法区内存也会溢出,会报oom,这里在1.7之前会报permgem oom,1.8则会报meta space oom,这是因为1.8中删除了堆中的永久代,转而使用元数据区。内存泄漏一般是因为对象被引用无法回收,比如一个集合中存着很多对象,可能你在外部代码把对象的引用置空了,但是由于对象还被集合给引用着,所以无法被回收,导致内存泄漏。测试也很简单,就在集合里添加对象,添加完以后把引用置空,循环操作,一会就会出现oom异常,原因是内存泄漏太多了,导致没有空间分配新的对象。常见调试工具命令行工具有jstack jstat jmap 等,jstack可以跟踪线程的调用堆栈,以便追踪错误原因。jstat可以检查jvm的内存使用情况,gc情况以及线程状态等。jmap用于把堆栈快照转储到文件系统,然后可以用其他工具去排查。visualvm是一款很不错的gui调试工具,可以远程登录主机以便访问其jvm的状态并进行监控。class文件结构class文件结构比较复杂,首先jvm定义了一个class文件的规则,并且让jvm按照这个规则去验证与读取。开头是一串魔数,然后接下来会有各种不同长度的数据,通过class的规则去读取这些数据,jvm就可以识别其内容,最后将其加载到方法区。JVM的类加载机制jvm的类加载顺序是bootstrap类加载器,extclassloader加载器,最后是appclassloader用户加载器,分别加载的是jdk/bin ,jdk/ext以及用户定义的类目录下的类(一般通过ide指定),一般核心类都由bootstrap和ext加载器来加载,appclassloader用于加载自己写的类。双亲委派模型,加载一个类时,首先获取当前类加载器,先找到最高层的类加载器bootstrap让他尝试加载,他如果加载不了再让ext加载器去加载,如果他也加载不了再让appclassloader去加载。这样的话,确保一个类型只会被加载一次,并且以高层类加载器为准,防止某些类与核心类重复,产生错误。defineclass findclass和loadclass类加载classloader中有两个方法loadclass和findclass,loadclass遵从双亲委派模型,先调用父类加载的loadclass,如果父类和自己都无法加载该类,则会去调用findclass方法,而findclass默认实现为空,如果要自定义类加载方式,则可以重写findclass方法。常见使用defineclass的情况是从网络或者文件读取字节码,然后通过defineclass将其定义成一个类,并且返回一个Class对象,说明此时类已经加载到方法区了。当然1.8以前实现方法区的是永久代,1.8以后则是元空间了。JVM虚拟机字节码执行引擎jvm通过字节码执行引擎来执行class代码,他是一个栈式执行引擎。这部分内容比较高深,在这里就不献丑了。编译期优化和运行期优化编译期优化主要有几种1 泛型的擦除,使得泛型在编译时变成了实际类型,也叫伪泛型。2 自动拆箱装箱,foreach循环自动变成迭代器实现的for循环。3 条件编译,比如if(true)直接可得。运行期优化主要有几种1 JIT即时编译Java既是编译语言也是解释语言,因为需要编译代码生成字节码,而后通过解释器解释执行。但是,有些代码由于经常被使用而成为热点代码,每次都编译太过费时费力,干脆直接把他编译成本地代码,这种方式叫做JIT即时编译处理,所以这部分代码可以直接在本地运行而不需要通过jvm的执行引擎。2 公共表达式擦除,就是一个式子在后面如果没有被修改,在后面调用时就会被直接替换成数值。3 数组边界擦除,方法内联,比较偏,意义不大。4 逃逸分析,用于分析一个对象的作用范围,如果只局限在方法中被访问,则说明不会逃逸出方法,这样的话他就是线程安全的,不需要进行并发加锁。1JVM的垃圾回收1 GC算法:停止复制,存活对象少时适用,缺点是需要两倍空间。标记清除,存活对象多时适用,但是容易产生随便。标记整理,存活对象少时适用,需要移动对象较多。2 GC分区,一般GC发生在堆区,堆区可分为年轻代,老年代,以前有永久代,现在没有了。年轻代分为eden和survior,新对象分配在eden,当年轻代满时触发minor gc,存活对象移至survivor区,然后两个区互换,等待下一场gc,当对象存活的阈值达到设定值时进入老年代,大对象也会直接进入老年代。老年代空间较大,当老年代空间不足以存放年轻代过来的对象时,开始进行full gc。同时整理年轻代和老年代。一般年轻代使用停止复制,老年代使用标记清除。3 垃圾收集器serial串行parallel并行它们都有年轻代与老年代的不同实现。然后是scanvage收集器,注重吞吐量,可以自己设置,不过不注重延迟。cms垃圾收集器,注重延迟的缩短和控制,并且收集线程和系统线程可以并发。cms收集步骤主要是,初次标记gc root,然后停顿进行并发标记,而后处理改变后的标记,最后停顿进行并发清除。g1收集器和cms的收集方式类似,但是g1将堆内存划分成了大小相同的小块区域,并且将垃圾集中到一个区域,存活对象集中到另一个区域,然后进行收集,防止产生碎片,同时使分配方式更灵活,它还支持根据对象变化预测停顿时间,从而更好地帮用户解决延迟等问题。
  • [软件平台] 【MDC300产品】【惯导功能】导远570D惯导抽象是走UART还是CAN
    【功能模块】惯导数据抽象【操作步骤&问题现象】1、导远570D惯导通过UART连接到MDC300,并启动ins惯导数据抽象进程2、ins惯导数据抽象没有发出数据3、用mdc-tool监控UART确定MCU是有收到惯导发来的串口数据并转发给HOST的4、用MCD Tool工具查看发现InsCmHostProcess进程通过someip接收的是CAN数据而非UART数据问题:ins惯导数据抽象到底是接收CAN数据还是UART数据?(MDC的软件版本为105)【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [参赛经验分享] 2019华为云数据库TaurusDB性能挑战赛亚军参赛总结
    缘起TaurusDB是华为云自主研发的最新一代云原生分布式数据库,完全兼容mysql8.0,采用计算与存储分离、日志即数据的架构设计,支持1写15读,性能达到原生Mysql的7倍。Taurus构建在共享分布式存储上,存储空间最高达128T,能跨AZ部署,具有可靠、高性能、易伸缩等特性。华为云TaurusDB性能挑战赛是由华为云主办的数据库领域顶级赛事,大赛将综合科研教学成果及商业领域需求,探索数据库领域的技术问题的可行性,为需求方和开发者提供联接的桥梁;并联合合作伙伴,搭建一个技术交流、人才培养、机遇共创数据库开发者平台和生态。听起来很激动人心吧,关键是奖金也很多, 同时还能刷脸! 最早是听一个同事说起的,恰好本人的工作也是“新一代数据库”开发,看着这么多人工智能的比赛不能参加,只能后悔自己选错了专业,但是机会来了,终于有自己擅长的领域了,心想不能错过这次机会,一边默默加了收藏。比赛内容一句话概括,就是实现一个kvstore,初赛是单机引擎,复赛要求存储计算分离,并且kv都是定长的,如果通用性做得好,可以直接用作块存储。题目初赛和复赛稍有区别,这里只说复赛的题目。存储引擎K,V都是定长的,key 8bytes, value 4K bytes。 测试分为三轮,第一轮, 16个线程写,每个线程写400万次;第二轮,16个线程读,每个线程按顺序读取400万次;第三轮,16个线程读,每个线程逆序读取400万次。第三轮的逆序是局部随机,总体逆序,也即在10M的数据范围内随机读,然后以10M为单位逆序推进。 成绩就是看总时间,时间越短越好。前面已经说过,初赛要实现的是单机引擎,复赛要实现的是存储计算分离的引擎,最大的限制是计算节点不能持久化任何数据。预选赛不得不说,华为可真会玩,这次预选赛竟然还要做题,并且是必须要学习他们的资料才能通过的题目,题目包括数据库的基本常识,但是也有产品介绍,经过深夜两个小时的学习,我算是第一次知道了这么多种不同的产品究竟是干嘛的。第一次答题还错了一道,以防万一,又做了一遍,这次错了两道,算了不玩了,95分也够了,睡觉去。但是心里总是不舒服,究竟是哪错了,又看了一遍,原来是幻读的理解这道题错了。初赛不出意外,预选赛顺利通过,初赛就开始写代码了,初赛只要写对差不多就可以晋级复赛。 当然任何一个程序员都不会满足刚好够用的状态,因此正常能想到的优化都加上了: 比如Direct IO, 多文件做数据分区,引入写buffer,比较频繁修改元数据,读取使用自己实现的page cache,以免4K读不能打满磁盘带宽。复赛复赛要求存储计算分离, 我认真做了一些分析,同时也对他们的硬件做了测试,分析下来,这个比赛比拼的点和我平时在工作中要追求的还是很不一样的。 平时无论做什么系统,都是在延迟差不多的情况下,把吞吐量做高,这次比赛最关键的一个点是:延迟第一,延迟是最重要的,特别是写的阶段,因为并发只有16,想通过聚合换吞吐量都不好使了。首先考虑持久化方法:4K恰好能对齐IO,所以key和value要分开存储,      想使用rocksdb之类的存储引擎即使不被禁止也是没有竞争力的。value非常随机,基本不用考虑压缩,就算有一点点好处,实现起来也太复杂了。再次考虑如何优化IO:SSD的IO吞吐量高于4K * iops, 不管是读还是写,IO聚合是必须的。单文件会遇到文件系统瓶颈,需要多文件, 也即要对数据做partition。关于同步IO还是异步IO的选择, 因为延迟优先,所以应该选同步IO。最后考虑网络框架:首先,我直接放弃考虑任何已有的网络框架,因为这是benchmark, 再小的开销也是开销,都会让程序变慢。其次,实际上任何IO      multiplexing的框架都会造成额外的延迟,不仅复杂,也是得不尝失的。综上,我决定就使用最简单的阻塞式IO来做socket通信。总体设计计算节点和存储节点分工, 关键就是索引维护在哪?经过思考, 决定索引维护在计算节点。 索引的内容是: key -> <file_id, pos>, 索引在计算节点的内存中维护为hash表。因为build索引大概需要400ms,写入再读取index比重新构建一遍index时间还长, 所以索引不需要持久化, 这其实是一个反直觉的决定。 计算节点在发送第一个读请求之前,会从存储节点把所有写入的key和pos发送过来, 然后由计算节点构造索引。存储节点起16个线程,listen在16个不同的端口。计算节点也会有16个线程, 每个计算节点的线程只会连接一个存储节点的端口,从而和一个存储节点线程通信。写入请求:写入过程不同的线程完全独立,每个线程负责一部分数据。请求发到存储节点后由接受请求的线程写入。读取过程:读取过程每个存储线程会读取任意一个线程写入的数据。由计算节点指定要读取那个分区的数据。也即写请求发到哪个存储线程,就由那个存储线程写入,每个存储线程实际上就对应了一个数据分区。 读请求发到一个存储线程,它可能要跨线程读取其它分区的数据。存储文件存储节点把数据分为16个partition,每个partition由一个线程负责写入。 每个partition共三个文件: 之所以有三个文件,是因为首先key和value要分开存储,这就要用两个文件;其次为了优化写,额外引入了文件做写入buffer。文件命名规则如下, 以第一个分区为例:00.k: 保存写入的key。 fallocate 4M * 8B, mmap到内存。00.v:      保存写入的value。fallocate 4M *      4K, DIO读写。00.b:      用作写入buffer。fallocate 16K      buffer + 4K 元数据,mmap到内存。写入原子性先写key和value,再更新key count。 key count改成功,则写成功;否则写失败,下次重启进程当作这次写没发生过。换句话说,key count改成功实际上表示这次写入commit成功。key count记录在00.k的第一个8字节, 如前所诉, 00.k是mmap到内存的,所以更新key count是没有什么代价的。value先记录到写buffer里,然后批量刷到00.v文件。key file内容如下:key countk1k2k3k4k5k6……value file内容如下:value countv1v2v3v4v5v6……buffer file内容如下:b0b1b2b3…………b15flushed poswrite buffer也是mmap到内存的, 前64K记录数据。 紧跟64K的8个字节记录flushed pos。 因为mmap必须以page为单位,所以实际内存占用64K + 4K。 它实际上是一个mmap持久化的ring buffer。 Ring Buffer的元数据包括filled pos和flushed pos。 filled pos由key count可以算出来, 所以不需要单独再记。build indexbuild index实际上是构造一个key -> offset的hash表。 要在400ms内完成build index,难点是如何并行:基本思路是把key做partition,每个partition内独立构造hash。这本质上是一个MapReduce的过程。map阶段: 并行划分range, 16个线程,每个线程负责处理一个key文件。reduce阶段: 并行构造hash, 16个线程,每个线程处理一个range。线程同步: 第二阶段开始之前要等第一全部完成,也即两个Stage之间是个Barrier, 这个同步过程和map reduce的shuffle是类似的。读取cache的实现cache是value的镜像,value文件分成了16个,cache也对应分成16组。 因为顺序和热点访问模式对cache都很友好,cache不需要特别大,每一组cache大小为64M。为了应对热点读,cache的最小单元设为16M,借用CPU cache的术语,我把它叫cache line,这是一个很大的值。 cache的内存因为是定长的,所以通过mmap一次申请好,不需要动态分配。 cache的索引本质是一个hash,但是不用解决冲突, 用file offset直接计算得到索引的下标。对16组cache中的每一组来说,有4个cache line, 每个cache line有三种状态,用一个uint32_t表示:Invalid: UINT32_MAXLocked:      UINT32_MAX - 1Valid:      file_offset »12cache line的状态被叫做indexStat,它被单独记在另外一个小数组里。读取不用加锁,使用double check即可:读取之前检查IndexStat有效,并且offset和我要读取的内容匹配。拷贝cache line的内容到私有buffer。拷贝完cache line的内容后再检查IndexStat是否发生过变化, 如果indexStat没有变化过,则说明我们拷贝的cache      line内容是对的。为了让上述double check生效, 更新cache的线程需要在写入之前把indexStat改为Locked,更新完成后再把indexStat改为有效值。关于网络通信关于网络框架还有一个问题,就是是否有必要用UDP,犹豫之下,觉得UDP还是会更复杂,稳妥起见,选择了TCP。后来得知在测试环境里UDP是不同的,也就释然了。但是TCP看起来开箱即用,用起来却暗藏机关,至少要调整以下三个参数:tcp_nodelaytcp_quickacksend/recv      buf最后为了规避TCP的流控,我们要避免另外两个坑:避免发送太快,超过交换机的队列长度,从而导致丢包,因为TCP的工作方式就是,只要你给它数据,它就会不停的加大,发送窗口,直到发生丢包,然后触发限流。要避免这种情况,本质是要限制TCP的连接数, 因为只要连接数限制住了,发送窗口总长度也就限制住了。避免TCP”冷却”之后重新进入慢启动状态,如果有root权限,是可以通过sysctl关闭slow start的,但是我们没法控制系统参数,这就要求我们一个连接最好要不停的发包,不要停下来。有人定义了packet格式,而我遵循一切从简的原则,没有实现通常RPC要有的功能:没有定义pcode,因为整个通信过程就只有读和写两种request,每一个socket只用来发送一种类型的request,如果要发送不同的request,只需要切换socket即可。没有实现序列化,因为request很简单,只需要把结构体直接发送到socket即可。细节决定性能细节也就是很多关于性能的小点:首先关于文件IO: 同步IO比异步IO延迟要小; open的时候加入O_NOATIME避免读取操作也修改元数据其次关于内存分配: 分配完内后提前触发page fault至少可以让性能更稳定,如果不能提高性能的话;      分配内存可以尝试hugepage, 失败之后再使用4K page; mmap的内存可以不用清零最好关于CPU, 绑核,通用可以让延迟更稳定, 理论上对性能是有提升的。正确性测试如前所属,我基本上是一切从简,但即使如此,提交了好多次,都通不过正确性测试。为了排查问题,专门写了一个随机的验证程序,这个比官方的测试强度高多了。确实发现了一个readv之后更新iovec的bug,还有一处cache的bug。 事后看来,在这个测试上花的时间非常值得,否则我可能到最后都没有成绩。总结这次比赛感觉有点像马拉松,听完大家的分享,感觉每个人的时间都比较有限,大家都是争分夺秒,每个人都有一些优化没来得及试。本人最大的一个遗憾是读预取没有实现。另外一个是写的过程中网络和IO并行化做的不好。最后听了第一名的方案,深有体会,要想拿到最好的成绩,需要把写和读分开考虑,同时把网络传输和本地IO分开考虑。单纯从工程上说,我的代码有一些特有的风格, 简单来说就是总爱重复造轮子,不愿意引入依赖:没有用pthread mutex/cond,全部用atomic      ops + futex没有spin,等待的地方都有睡眠和唤醒机制另外,本人虽然工作中用C++, 但是我情愿用plain old C, 所以我很少用高级C++特性。如果未来还做这种系统实现的比赛,希望有可以利用RDMA的和NVM的比赛, 毕竟,新硬件总是有更多的可能。当然比赛的设置要考虑引入更多自由度,这样会更加有趣。
  • [行业资讯] 英特尔面向物联网发布第12代英特尔酷睿处理器
      英特尔在近期举行的2022年国际消费类电子产品展览会(CES 2022)上发布了第12代英特尔酷睿处理器(代号Alder Lake S系列和H系列)。这是英特尔首个针对边缘进行优化的处理器家族,该款处理器采用的高性能混合架构将性能核与能效核以及英特尔硬件线程调度器(Intel Thread Director)有机整合在一起,对加速物联网应用创新进行优化,为零售、制造、医疗和视频客户提供更多的核数,先进的图形、媒体、显示和人工智能性能,并为用户提供在价格、性能和功耗上更加丰富的选择。  从支持各类客户端以提供丰富的视觉体验,到在单个边缘平台上运行混合关键工作负载,第12代英特尔酷睿处理器可以帮助我们的客户向前迈进一大步,在边缘侧打造软件定义的世界。  ——John Healy  英特尔公司物联网事业部副总裁兼  平台管理与客户赋能部门总经理  第12代英特尔酷睿处理器家族采用了英特尔全新的高性能混合架构和Intel 7制程工艺,能够提供比其他零售、医疗、制造和视频解决方案更胜一筹的关键特性。这些特性包括:  ● 适用于重型物联网工作负载的高算力性能和灵活性;  ● 无需额外硬件即可获得更强大的人工智能和深度学习能力;  ●针对嵌入式使用条件的可靠性;  ●支持4K和8K显示器的增强型集成显卡处理单元;  ●基于硬件的安全性,可保护易受攻击的物联网设备免受攻击;  ●强大的生态系统和开源社区,能够支持多种操作系统,使物联网客户可以选择适合其目标应用的操作系统。  第12代英特尔酷睿台式机处理器  (Alder Lake S系列)  与第10代智能英特尔 酷睿处理器相比,面向物联网的第12代英特尔酷睿台式机处理器将单线程性能提升高达1.36倍①,将多线程性能提升高达1.35倍②,将图形性能提升高达1.94倍③,并将GPU图像分类推理性能提升高达2.81倍④。面向物联网的第12代英特尔酷睿台式机处理器包含由英特尔 Xe架构驱动的英特尔超核芯显卡770,支持显示虚拟化和多达四个独立显示器。此外,对PCIe 5.0/PCIe 4·0和DDR5/DDR4内存的支持,结合安全性和可管理性功能以及对人工智能支持,将有助于提高生产力并推动物联网应用领域的未来创新。物联网相关机型在35W至65W的热设计功耗下可提供最多16个核和24个线程,并具备实时功能、长寿命特性和长期的软件支持。  这些处理器可为广泛行业的发展提供助益。  ●零售、银行、酒店餐饮和教育客户将能够借此改善工作负载融合,挖掘无接触零售和信息显示屏的价值,改进交互式显示体验;  ● 工业制造客户可以借此更好地发挥工业PC、边缘服务器、高级控制器、机器视觉系统和虚拟化控制平台的价值;  ● 医疗客户将能够在边缘侧提供增强的超声成像、医疗推车、内窥镜和临床设备;  ● 视频客户将能够发挥AI计算盒的分析功能,发挥网络录像机和视频墙的更大效用;  第 12 代英特尔 酷睿高性能移动版处理器  (Alder Lake H系列)  根据估算,与第11代智能英特尔酷睿处理器相比,面向物联网的第12代英特尔酷睿高性能移动版处理器可将单线程性能提升高达1.04倍⑤,将多线程性能提升高达1.18倍⑥,并将图形性能提升高达2.29倍⑦。这些处理器的热设计功耗在35W至45W间,可提供多达14个核和20个线程。同时,英特尔还发布了热设计功耗范围在15W到28W之间的第12代英特尔酷睿处理器U系列和P系列。四个显示通道搭配英特尔锐炬 Xe显卡丰富的沉浸式体验,能够为全面的视频墙部署提供支持,与此同时,人工智能的加持则能充分满足推理和机器视觉场景的需求。  这些处理器将为各行各业提供广泛的优势,这些优势包括:  ● 零售、银行、酒店和教育客户将能够通过四个显示通道改善视频墙的工作负载整合能力,提升使用体验,并能够为交互式电子白板和信息显示屏提供人工智能分析能力;  ● 工业制造客户能够借助人机交互面板和工业PC,更好地利用机器视觉进行零件的缺陷检测;  ● 医疗客户将通过人工智能及分析技术改进超声波等设备中的医学成像效果,以协助医生实施诊断、开展远程医疗和推广机器人技术;  ● 视频客户将能够在边缘和具有人工智能功能的网络录像机中部署机器视觉技术,并将其用于智能建筑、智能工厂和智慧城市等应用当中。  关于上市计划  目前已有80多个物联网客户参与了英特尔关于第12代英特尔酷睿处理器的抢先体验计划。第12代英特尔酷睿台式机处理器(Alder Lake S系列)预计将于2022年1月上市;第12代英特尔酷睿高性能移动版处理器(Alder Lake H系列)预计将于2022年4月上市。  向上滑动阅览  免责声明  实际性能受使用情况、配置和其他因素的差异影响。有关更多信息,请访问 www.Intel.com/PerformanceIndex  性能测试结果基于配置信息中显示的日期进行测试,且可能并未反映所有公开可用的安全更新。关于工作负载与配置的信息,请访问intel.com/PerformanceIndex。结果可能不同。没有任何产品或组件是绝对安全的。第12代英特尔酷睿高性能移动版处理器的结果由估算或模拟得出。测量了第12代英特尔酷睿处理器的结果。具体成本和结果可能不同。  代际比较显示,该平台实现了显著的性能提升A  ① SPECrate2017_int_base (1-copy) 使用英特尔编译器19 update 4进行的测量显示,单线程性能提升最高 1.36 倍  ② SPECrate2017_int_base (n-copy) 使用英特尔编译器19 update 4进行的测量显示,多线程性能提升最高 1.35 倍  ③ 图形性能提升最高1.94倍,3DMark Ver.2.11.6846 - Fire Strike - 图形得分  ④ GPU图像分类推理性能提升最高2.81倍B MLPerf v1.1 ResNet50-离线 - int8 - GPU  A 与上一代物联网S系列Comet Lake S处理器英特尔 酷睿 i9-10900E相比。资料来源:截至2021年11月的英特尔内部测试。关于工作负载与配置的信息,请访问intel.com/PerformanceIndex。结果可能不同。  B MLPerf 边缘推理v1.1推理ResNet-v1.5;结果未经过MLCommons Association验证。MLPerf名称和标识是MLCommons Association在美国和其他国家或地区的商标。保留所有权利。未经授权,不得使用。更多信息请访问www.mlcommons.org。资料来源:截至2021年11月的英特尔内部测试。工作负载和配置详情请参见备用页。结果可能不同。  ⑤ SPECrate2017_int_base (1-copy)使用英特尔编译器19 update 4进行的测量显示,单线程性能提升最高1.04(e)倍  ⑥ SPECrate2017_int_base (n-copy)使用英特尔编译器19 update 4进行的测量显示,多线程性能提升最高1.18(e)倍  ⑦ 图形性能提升最高2.29(e)倍,3DMark - Fire Strike - 图形得分  资料来源:英特尔 酷睿 i7-12800HE 得分由英特尔估算得出,截至2021年11月。前期芯片估算存在+/- 7%的误差
总条数:601 到第
上滑加载中