• [其他] 主动预防-DWS-纯软core配置方案
    以下两种方式二选一即可:自动化配置工具:cid:link_0手动配置步骤:1、以root用户登录OMS主节点切换至tmp路径下su - rootcd /tmp2、在新建目录下创建core.sh脚本,内容如下:sed -i '/^.*hard.*core.*$/d' /etc/security/limits.confsed -i '/^.*soft.*core.*$/d' /etc/security/limits.confecho "* soft core unlimited" >> /etc/security/limits.confecho "* hard core unlimited" >> /etc/security/limits.confsed -i "/fs.suid_dumpable/d" /etc/sysctl.confecho "core" > /proc/sys/kernel/core_patternsed -i '/^kernel.core_pattern.*$/d' /etc/sysctl.confecho "kernel.core_pattern=core" >> /etc/sysctl.confecho "fs.suid_dumpable = 1" >> /etc/sysctl.confecho "kernel.core_uses_pid=0" >> /etc/sysctl.conf/sbin/sysctl -p3、在新集群OMS主节点借助批量执行工具,配置集群所有节点core,用root执行如下命令即可分开命令执行core配置dos2unix /tmp/core.shcd /opt/FusionInsight_SetupTool/preinstall/tools/cluster./clusterscp.sh put /tmp/core.sh /tmp/./clustercmd.sh "sh /tmp/core.sh" 4、kill om_monitor重启集群后检查core配置是否生效gs_ssh -c "killall -u omm om_monitor"检查配置是否生效:1) ps -ef|grep masterx,找到进程号2) cd /proc/进程号。3) view limits4) cat /proc/sys/kernel/core_pattern5、 验证是否成功kill -11 从备DN进程号,查看对应实例目录下是否有core文件产生。 
  • [工具链] 【MDC 300产品】【MDS】MDS中如何同时运行两个进程
    MDS想要同时运行客户端client和服务端server的进程,一个是发送数据,一个是接受数据,如何同时运行观察运行结果呢
  • [新手课堂] 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将堆内存划分成了大小相同的小块区域,并且将垃圾集中到一个区域,存活对象集中到另一个区域,然后进行收集,防止产生碎片,同时使分配方式更灵活,它还支持根据对象变化预测停顿时间,从而更好地帮用户解决延迟等问题。
  • [分布式存储] 【故障案例】重启Ceph集群的OSD进程失败
    现象描述Ceph集群测试读写性能,进行一轮测试后。重启集群OSD节点,继续开展读写测试,测试工具报错:java.lang.RuntimeException: Slave hd2-0 prematurely terminated.at Vdb.common.failure[common.java:335)at Vdb.SlaveStarter.startSlave(SlaveStarter.java:198)at Vdb.SlaveStarter.run(SlaveStarter.java:47)具体信息如下图所示。查看Ceph集群状态发现部分OSD状态为down,如图所示。处理步骤查看Ceph日志时发现往Ceph分配内存时失败,怀疑在OSD进程在获取内存是有异常。输入如图命令发现“osd_memory_target”的值并非官方发布的默认的4G。在ceph.conf文件中添加“osd_memory_target = 4294967296” ,使分配给每个osd的内存限制为4GB。将修改后的文件推送到其他节点。ceph-deploy --overwrite-conf admin ceph1 ceph2 ceph3 client1 client2 client3重启集群。systemctl restart ceph.target
  • [技术干货] Java中容易混淆的基础知识[转载]
    面向对象三大特性:继承,封装,多态封装3中修饰符:public,private,protected,给位于同一个或不同包中的对象赋予了不同的访问权限封装的一些好处通过隐藏对象的属性来保护对象内部的状态提高代码的可用性,可维护性提高模块化继承给对象提供从基类获取字段和方法的能力,基础提高代码的重用性,可以在不修改类的情况下添加新的特性多态多态就是同一函数在不同类中有不同的实现;面向对象的多态性,即“一个接口,多个方法”。多态性体现在基类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。多态性允许一个接口被多个同类使用,弥补了单继承的不足。final,finally,finalize的区别finalfinal可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个 常量不能被重新赋值。finallyfinally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码放入finally代码块中,表示不管是 否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。finalizefinalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调 用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。int和integer的区别intint是基本数据类型integerinteger是其包装类,是一个类为了在各种类型中转换,通过各种方法调用int a = 0; String result = Integer.toString(a); //将int转换为String重载与重写的区别override重写overload重载抽象类和接口的区别接口时公开的,不能有私有的方法和变量,抽象类可以有私有的方法或私有的变量抽象类和接口的区别反射的用途以及实现反射机制所提供的功能在运行时创造一个类的对象;判断一个类所具有的成员变量和方法调用一个对象的方法生成动态代理Java反射的主要功能:确定一个对象的类取出类的modifiers,数据成员,方法,构造类,超类在运行时刻调用动态对象的方法.创建数组,数组大小和类型反射机制的理解和应用自定义注解的场景及实现登陆、权限拦截、日志处理,以及各种Java 框架,如Spring,Hibernate,JUnit 提到注解就不能不说反射,Java自定义注解是通过运行时靠反射获取注解。实际开发中,例如我们要获取某个方法的调用日志,可以通过AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志 注解,就进行日志记录。http请求的get和post方式的区别原理区别一般我们在浏览器输入一个网址访问网站都是GET请求;再FORM表单中,可以通过设置Method指定提交方式为GET或者POST提交方式,默认为GET提交方式。HTTP定义了与服务器交互的不同方法,其中最基本的四种:GET,POST,PUT,DELETE,HEAD,其中GET和HEAD被称为安全方法,因为使用GET和HEAD的HTTP请求不会产生什么动作。不会产生动作意味着GET和HEAD的HTTP请求不会在服务器上产生任何结果。但是安全方法并不是什么动作都不产生,这里的安全方法仅仅指不会修改信息。根据HTTP规范,POST可能会修改服务器上的资源的请求。比如CSDN的博客,用户提交一篇文章或者一个读者提交评论是通过POST请求来实现的,因为再提交文章或者评论提交后资源(即某个页面)不同了,或者说资源被修改了,这些便是“不安全方法”。请求的区别GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在 客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方式传递。POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。参考:get和post请求方式的区别seesion与cookie的区别cookie数据存放在客户的浏览器上,session数据放在服务器上.cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)JDBC流程加载JDBC驱动程序:在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.lang.Class类的静态方法forName(String className)实现。例如:try{ //加载MySql的驱动类 Class.forName("com.mysql.jdbc.Driver") ; }catch(ClassNotFoundException e){ System.out.println("找不到驱动程序类 ,加载驱动失败!"); e.printStackTrace() ; 成功加载后,会将Driver类的实例注册到DriverManager类中。提供JDBC连接的URL连接URL定义了连接数据库时的协议、子协议、数据源标识。书写形式:协议:子协议:数据源标识协议:在JDBC中总是以jdbc开始 子协议:是桥连接的驱动程序或是数据库管理系统名称。数据源标识:标记找到数据库来源的地址与连接端口。例如:jdbc:mysql://localhost:3306/test? useUnicode=true&characterEncoding=gbk;useUnicode=true;(MySql的连接URL)表示使用Unicode字符集。如果characterEncoding设置为 gb2312或GBK,本参数必须设置为true 。characterEncoding=gbk:字符编码方式。创建数据库的连接java.sql.DriverManager Connection代表一个数据库的连接。使用DriverManager的getConnectin(String url , String username , String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来获得。例如: //连接MySql数据库,用户名和密码都是rootString url = "jdbc:mysql://localhost:3306/test" ; String username = "root" ; String password = "root" ; try{ Connection con = DriverManager.getConnection(url , username , password ) ; }catch(SQLException se){ System.out.println("数据库连接失败!"); se.printStackTrace() ; • 要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3 种类型:1、执行静态SQL语句。通常通过Statement实例实现。2、执行动态SQL语句。通常通过PreparedStatement实例实现。3、执行数据库存储过程。通常通过CallableStatement实例实现。具体的实现方式:Statement stmt = con.createStatement() ; PreparedStatement pstmt = con.prepareStatement(sql) ; CallableStatement cstmt = con.prepareCall("{CALL demoSp(? , ?)}") ;执行SQL语句Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate 和execute1、ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句 ,返回一个结果集(ResultSet)对象。2、int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的 语句。 具体实现的代码:ResultSet rs = stmt.executeQuery(“SELECT * FROM …”) ; int rows =stmt.executeUpdate(“INSERT INTO …”) ; boolean flag = stmt.execute(String sql) ;处理结果两种情况:1、执行更新返回的是本次操作影响到的记录数。2、执行查询返回的结果是一个ResultSet对象。• ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些 行中数据的访问。• 使用结果集(ResultSet)对象的访问方法获取数据:while(rs.next()){String name = rs.getString(“name”) ;String pass = rs.getString(1) ; // 此方法比较高效}(列是从左到右编号的,并且从列1开始)关闭JDBC对象操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声 明顺序相反:1、关闭记录集2、关闭声明3、关闭连接对象 if(rs!=null){ // 关闭记录集 try{ rs.close(); }catch(SQLException e){e.printStackTrace(); } }try{ stmt.close(); } }catch(SQLException e){e.printStackTrace(); } if(conn!=null){ // 关闭连接对象 try{ conn.close(); }catch(SQLException e){ e.printStackTrace();20} }MVC思想M:Model 模型V:View 视图C:Controller控制器模型就是封装业务逻辑和数据的一个一个的模块,控制器就是调用这些模块的(java中通常是 用Servlet来实现,框架的话很多是用Struts2来实现这一层),视图就主要是你看到的,比如JSP 等.当用户发出请求的时候,控制器根据请求来选择要处理的业务逻辑和要选择的数据,再返回去 把结果输出到视图层,这里可能是进行重定向或转发等.equals 与 == 的区别值类型(int,char,long,boolean等)都是用==判断相等性。对象引用的话,判断引用所指的对象 是否是同一个。equals是Object的成员函数,有些类会覆盖(override)这个方法,用于判 断对象的等价性。例如String类,两个引用所指向的String都是"abc",但可能出现他们实际对应的对象并不是同一个(和jvm实现方式有关),因此用 ==判断他们可能不相等,但用equals判断一定是相等的。线程创建线程的方法及实现Java中创建线程主要有三种方式:一、继承Thread类创建线程类(1) 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。(2) 创建Thread子类的实例,即创建了线程对象。(3) 调用线程对象的start()方法来启动该线程。package com.thread; public class FirstThreadTest extends Thread{ int i = 0; //重写run方法,run方法的方法体就是现场执行体public void run() { for(;i<100;i++){ System.out.println(getName()+" "+i); } } public static void main(String[] args) { for(int i = 0;i< 100;i++) { System.out.println(Thread.currentThread().getName()+" "+i): if(i==20) { new FirstThreadTest().start(); new FirstThreadTest().start(); } } }二、通过Runnable接口创建线程类(1) 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。(2) 创建 Runnable实现类的实例的target来创建Thread对象,该Thread对象才是真正的线程对象。(3)调用线程对象的start()方法来启动该线程package com.thread; public class RunnableThreadTest implements Runnable { private int i; public void run() { for(i = 0;i <100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } public static void main(String[] args) { for(int i = 0;i < 100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); if(i==20) { RunnableThreadTest rtt = new RunnableThreadTest(); new Thread(rtt,"新线程1").start(); new Thread(rtt,"新线程2").start(); } } } } 三、通过Callable和Future创建线程(1) 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。(2) 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(3) 使用FutureTask对象作为Thread对象的target创建并启动新线程。(4) 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值package com.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); 13 for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " 的循 环变量i的值" + i); if (i == 20) { new Thread(ft, "有返回值的线程").start(); } } try { System.out.println("子线程的返回值:" + ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public Integer call() throws Exception 34 { int i = 0; for (; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } return i; } }采用实现Runnable、Callable接口的方式创见多线程时,优势是:线程类只是实现了 接口或 接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同 一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面 向对象的思想。劣势是:编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。使用继承Thread类的方式创建多线程时优势是:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this 即可获得当前线程。劣势是:线程类已经继承了Thread类,所以不能再继承其他父类。sleep() 、join()、yield()的区别一、sleep()在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度 程序精度和准确性的影响。 让其他线程有机会继续执行,但它并不释放对象锁。也就是如 果有Synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕获异常比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一 个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级 的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的 线程有执行的机会。二、join()Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前, B不能工作。保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有 存活,则当前线程不需要停止。三、yield()yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方 法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态 后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也 和sleep()方法不同。再给大家推荐一本书:Java并发编程想要电子版也可以私信我线程池的几种方式newFixedThreadPool(int nThreads)创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数 量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程newCachedThreadPool()创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当 需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制newSingleThreadExecutor()这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会 创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行newScheduledThreadPool(int corePoolSize)创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。private static final Executor exec=Executors.newFixedThreadPool(50); Runnable runnable=new Runnable(){ public void run(){ ... } } exec.execute(runnable); Callable<Object> callable=new Callable<Object>() { public Object call() throws Exception { return null; } } Future future=executorService.submit(callable); future.get(); // 等待计算完成后,获取结果 future.isDone(); // 如果任务已完成,则返回 true future.isCancelled(); // 如果在任务正常完成前将其取消,则返回 true future.cancel(true); // 试图取消对此任务的执行,true中断运行的任务,false 允许正在运行的任务运行完成线程的生命周期新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态(1)生命周期的五种状态新建(new Thread)当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread t1=new Thread();就绪(runnable)线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队 等候得到CPU资源。例如:t1.start();运行(running)线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有 优先级更高的线程进入,线程将一直运行到结束。死亡(dead)当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态 等待执行。自然终止:正常运行run()方法后终止异常终止:调用**stop()**方法让一个线程终止运行堵塞(blocked)由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。正在等待:调用wait()方法。(调用motify()方法回到就绪状态)被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)锁机制线程安全线程安全是指要控制多个线程对某个资源的有序访问或修改,而在这些线程之间没有产生冲 突。在Java里,线程安全一般体现在两个方面:1、多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关 键字synchronized。如ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)。如果你在interator一个List对象时,其它线程remove一个element, 问题就出现了。2、每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类,而没有Java关键字支持,如像static、transient那样。volatle实现原理深入分析 Volatile 的实现原理悲观锁,乐观锁是一种思想。可以用在很多方面。比如数据库方面。悲观锁就是for update(锁定查询的行)乐观锁就是 version字段(比较跟上一次的版本号,如果一样则更新,如果失败则要重复读­比较­写的操作。)JDK方面:悲观锁就是sync乐观锁就是原子类(内部使用CAS实现)本质来说,就是悲观锁认为总会有人抢我的。乐观锁就认为,基本没人抢。乐观锁是一种思想,即认为读多写少,遇到并发写的可能性比较低,所以采取在写时先读出 当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重 复读­比较­写的操作。CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。CAS顶多算是乐观锁写那一步操作的一种实现方式罢了,不用CAS自己加锁也是可以的。乐观锁的业务场景及实现方式每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁, 但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不 进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁, 期间该数据可以被其他线程进行读写操作。乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可 能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量 的查询操作,降低了系统的吞吐量。最后都看到这了,给孩子一个三连支持一下吧,Java对初学者很友好;Java资源丰富,因为它可以解决不同的问题;Java有一个庞大而友好的社区;Java无处不在,因此更容易找到第一份工作;Java开发人员缺口大,薪水很高。只要你具备能力,薪资待遇你敢要公司就敢给。链接:https://bbs.huaweicloud.com/blogs/336625
  • [新手课堂] 释放占用端口
    1. 找到系统当前所有的端口使用 netstat 命令查找本机各端口的网络连接情况~]netstat -nulpt #结果如下Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address           Foreign Address         Statetcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:1997            0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTENtcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTENtcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTENtcp6       0      0 :::80                   :::*                    LISTENtcp6       0      0 :::22                   :::*                    LISTENtcp6       0      0 ::1:25                  :::*                    LISTENtcp6       0      0 :::3306                 :::*                    LISTEN这里我们要找的是 80与443 端口2. 找到对应端口在系统中的进程 ID(PID)依据查找到的 1997 端口找到对应进程, lsof -i :1997, 注意 : 冒号不要漏掉了lsof -i:80 #结果如下COMMAND    PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAMEtke-gatew 6360 root   10u  IPv6 6410452      0t0  TCP *:http (LISTEN)[root@VM-0-114-centos ~]# lsof -i:443COMMAND     PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAMEtke-gatew  7627 root    9u  IPv6 7717644      0t0  TCP *:https (LISTEN).........->172.80.31.2:https (ESTABLISHED)galaxy    25638 root    8u  IPv4 6276795      0t0  TCP  <机密省略>:57802-..........3. 使用 kill -9 [PID] 命令结束进程通过 lsof 命令我们找到了进程的 PID: 29416,接下来就是使用 kill -9 [PID] 把进程结束就好了kill -9 6360kill -9 25638kill -9 7627到这里就 OK 了,不过为了保险起见,再次执行 netstat -tln 确认是否结束了端口占用 前情提要在布置k8s的时候发现容器报错如~]kubectl get pods -A -o wide.....pot   nginx-ingress-nginx-ingress-85d747cfb4-9hk72   0/1   CrashLoopBackOff.....然后想describe查看了一下报错如下~]kubectl describe pod -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72......... Warning  BackOff  3m14s (x613 over 137m)  kubelet  Back-off restarting failed container最后logs查看发现端口被占用~]# kubectl logs pods -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72Error from server (NotFound): pods "pods" not found[root@VM-0-114-centos ~]# kubectl logs  -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72I1104 10:51:46.738108       1 main.go:169] Starting NGINX Ingress controller Version=1.6.3 GitCommit=b9378d562021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:80 failed (98: Address already in use)nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)2021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:443 failed (98: Address already in use).......2021/11/04 10:51:46 [notice] 19#19: try again to bind() after 500ms2021/11/04 10:51:46 [emerg] 19#19: still could not bind()nginx: [emerg] still could not bind()运用了以上步骤之后回复正常pot   nginx-ingress-nginx-ingress-85d747cfb4-9hk72   1/1     Running
  • [技术干货] 单例模式懒汉式,线程不安全
    下面的懒汉式是线程不安全的,支持懒加载,因为没有加锁 synchronized,所以严格意义上它并不算单例模式。样例代码:public class Singleton{    private static Singleton instance;    private Singleton(){    }    public static Singleton getInstance(){        if(instance == null){            return new Singleton();        }        return instance;    }}
  • [HPC] QE 6.8 移植指南(CentOS 8.2)
    1.1 介绍QUANTUM ESPRESSO是一种用于电子结构计算和材料建模的abinitio量子化学方法的软件套件,在GNU通用公共许可证下免费分发。它基于密度泛函理论,平面波基组和赝势(包括范数守恒和超软)。该程序主要在Fortran-90中编写,其中一些部分在C或Fortran-77中,是根据不同的独立开发的核心软件包的合并和重新设计以及一组旨在互操作的软件包构建的。基本软件包包括Pwscf,它解决了自洽的Kohn和Sham方程,获得了周期性实体,CP实现了Car-Parrinello分子动力学,PostProc实现了数据分析和绘图。关于附加封装,值得注意的是指出赝势生成的原子,PHonon封装,它实现了密度泛函扰动理论(DFPT),用于计算能量相对于原子位移和NEB的二阶和三阶导数。关于QUANTUM ESPRESSO的更多信息请访问QUANTUM ESPRESSO官网。语言:C++/Fortran。一句话描述:可扩展的计算化学工具。开源协议:GPL 2.0。建议的版本建议使用的版本为“QE 6.8”。1.2 环境要求硬件要求硬件要求如表2-1所示。硬件要求项目说明CPU鲲鹏920处理器。GPUNVIDIA Tesla V100。 软件要求软件要求如表2-2所示。软件要求项目版本下载地址QUANTUM ESPRESSO6.8https://github.com/QEF/q-e/archive/refs/tags/qe-6.8.tar.gzOpenBLAS0.3.6https://github.com/xianyi/OpenBLAS/archive/refs/tags/v0.3.6.tar.gzHyper MPI1.1.1https://support.huaweicloud.com/usermanual-kunpenghpcs/userg_huaweimpi_0010.htmlNVIDIA CUDA11.4https://developer.download.nvidia.com/compute/cuda/11.4.0/local_installers/cuda_11.4.0_470.42.01_linux_sbsa.runNVIDIA HPC SDK21.9https://developer.download.nvidia.com/hpc-sdk/21.9/nvhpc_2021_219_Linux_aarch64_cuda_11.4.tar.gz 操作系统要求操作系统要求如表2-3所示。操作系统要求项目版本下载地址CentOS8.2https://vault.centos.org/8.2.2004/isos/aarch64/CentOS-8.2.2004-aarch64-dvd1.isoKernel4.18.0-193https://vault.centos.org/8.2.2004/BaseOS/Source/SPackages/kernel-4.18.0-193.el8.src.rpm 1.3 移植规划数据本章节给出QE软件在移植过程中涉及到的相关软件安装规划路径的用途及详细说明。移植规划数据序号软件安装规划路径用途说明1-基础环境搭建中的各安装包安装路径。参考《HPC解决方案 基础环境搭建指导书》中“安装规划数据”章 节。2/path/to/qeQUANTUM ESPRESSO的安装规划路径。这里的安装规划路径只是一个举例说明,建议部署在共享路径中。现网需要根据实际情况调整,后续章节凡是遇到安装路径的命令,都以现网实际规划的安装路径为准进行替换,不再单独说明。3/usr/local/cuda-11.4NVIDIA CUDA的安装规划路径。4/path/to/openblasOpenBLAS的安装规划路径。5/opt/nvidia/hpc_sdkNVIDIA HPC SDK的安装规划路径。6/path/to/CASEQUANTUM ESPRESSO的算例文件存放规划路径。 1.4 配置编译环境前提条件使用SFTP工具将各安装包上传至服务器对应目录下。配置流程配置流程序号配置项说明1基础环境搭建参考《HPC解决方案 基础环境搭建指导书》中“集群场景环境搭建”章节。2禁用nouveau驱动参考2.4.1 禁用nouveau驱动。3安装NVIDIA CUDA参考2.4.2 安装NVIDIA CUDA组件。4安装Hyper MPI参考2.4.3 安装Hyper MPI。5安装NVIDIA HPC SDK参考2.4.4 安装NVIDIA HPC SDK。6安装OpenBLAS参考2.4.5 安装OpenBLAS。 1.4.1 禁用nouveau驱动操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令查看nouveau驱动是否已禁用。                    lsmod |grep nouveau若有回显信息,表示nouveau驱动未禁用,则执行步骤3。若无回显信息,表示nouveau驱动已禁用,则结束操作。步骤 3 执行以下命令禁用nouveau驱动。新建文件“disable-nouveau.conf”。                    vi /etc/modprobe.d/disable-nouveau.conf按“i”进入编辑模式,添加如下内容。                    blacklist nouveau                    options nouveau modeset=0按“Esc”键,输入:wq!,按“Enter”保存并退出编辑。步骤 4 执行以下命令备份并生成新的“initramfs”文件                    cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img                    dracut -f /boot/initramfs-$(uname -r).img $(uname -r)步骤 5 执行以下命令重启机器。                    reboot----结束1.4.2 安装NVIDIA CUDA组件操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令确认nouveau驱动是否已禁用。                    lsmod |grep nouveau若有回显信息,表示nouveau驱动未禁用,则执行步骤3~步骤5。若无回显信息,表示nouveau驱动已禁用,则结束操作。步骤 3 执行以下命令安装NVIDIA CUDA组件。                    wget https://developer.download.nvidia.com/compute/cuda/11.4.0/local_installers/cuda_11.4.0_470.42.01_linux_sbsa.run                    sudo shcuda_11.4.0_470.42.01_linux_sbsa.run步骤 4 执行以下命令配置环境变量。                    export PATH=/usr/local/cuda-11.4/bin:$PATH                    export LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH步骤 5 执行以下命令进行验证。                    nvcc --version若返回结果已包含NVIDIA CUDA组件版本信息,说明安装成功。----结束1.4.3 安装Hyper MPI操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令安装HUCX源码包和XUCG源码包。                    wget -c https://github.com/kunpengcompute/hucx/archive/refs/heads/huawei.zip -O hucx-huawei.zip                    wget -c https://github.com/kunpengcompute/xucg/archive/refs/heads/huawei.zip -O xucg-huawei.zip步骤 3 执行以下命令解压HUCX源码包和XUCG源码包。                    unzip hucx-huawei.zip                    unzip xucg-huawei.zip步骤 4 执行以下命令将XUCG源码包中的内容复制到HUCX源码包中的“src/ucg”目录下。                    cp -r xucg-huawei/* hucx-huawei/src/ucg/步骤 5 执行以下命令进入hucx-huawei目录。                    cd hucx-huawei步骤 6 执行以下命令设置环境变量。                    export SEC_CFLAGS="-Wno-error=deprecated-declarations -DHAVE___CLEAR_CACHE=1 -Wno-error=asm-operand-widths -Wno-error=implicit-int-float-conversion -Wno-error=format -Wno-error=tautological-pointer-compare"步骤 7 执行以下命令创建HUCX安装目录。                    mkdir -p /path/to/ucx步骤 8 执行以下命令安装HUCX。                    ./autogen.sh                    ./contrib/configure-opt --prefix=/path/to/ucx CFLAGS="$SEC_CFLAGS" CXXFLAGS="$SEC_CFLAGS"                    make -j32                    make install步骤 9 执行以下命令获取Hyper MPI源码包。                    wget -c https://github.com/kunpengcompute/hmpi/archive/refs/heads/huawei.zip -O hmpi-huawei.zip步骤 10 执行以下命令解压Hyper MPI源码包。                    unzip hmpi-huawei.zip步骤 11 执行以下命令进入hmpi-huawei目录。                    cd hmpi-huawei步骤 12 执行以下命令安装Hyper MPI。                    ./autogen.pl                    ./configure --prefix=/path/to/ompi --with-platform=contrib/platform/mellanox/optimized --enable-mpi1-compatibility --with-ucx=/path/to/ucx                    make -j16                    make install步骤 13 执行以下命令配置环境变量。                    export PATH=/path/to/ompi/bin:/path/to/ucx/bin:$PATH                    export LD_LIBRARY_PATH=/path/to/ompi/lib:/path/to/ucx/lib:$LD_LIBRARY_PATH                    export OPAL_PREFIX=/path/to/ompi/                    export INCLUDE=/path/to/ompi:$INCLUDE----结束1.4.4 安装NVIDIA HPC SDK操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令获取毕昇编译器软件包。                    wget https://developer.download.nvidia.com/hpc-sdk/21.9/nvhpc_2021_219_Linux_aarch64_cuda_11.4.tar.gz步骤 3 执行以下命令创建毕昇编译器安装目录(这里以/opt/compiler为例)。                    mkdir -p /opt/compiler步骤 4 执行以下命令将毕昇编译器压缩包拷贝到安装目录下。                    cp –r nvhpc_2021_219_Linux_aarch64_cuda_11.4 /opt/compiler步骤 5 执行以下命令进入压缩包目录。                    cd /opt/compiler步骤 6 执行以下命令解压缩软件包。                    tar -zxvf nvhpc_2021_219_Linux_aarch64_cuda_11.4.tar.gz步骤 7 执行以下命令解压目录。                    cd nvhpc_2021_219_Linux_aarch64_cuda_11.4/步骤 8 执行以下命令进行安装。                    ./install按“Enter”键确认安装位置,默认为“/opt/nvidia/hpc_sdk”,再按“1”选择单机安装,安装程序会自行找到之前安装的“cuda toolkit”,安装完成后按“Enter”键结束。步骤 9 执行以下命令配置NVIDIA HPC SDK的环境变量。                    module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/21.9步骤 10 执行以下命令进行验证。                    pgcc --version若返回结果已包含“PGI Compilers and Tools”字样,说明安装成功。----结束1.4.5 安装OpenBLAS操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令解压OpenBLAS安装包。                    tar -zxvf OpenBLAS-0.3.6.tar.gz步骤 3 执行以下命令进入解压后的目录。                    cd OpenBLAS-0.3.6步骤 4 执行以下命令编译安装OpenBLAS。                    make                    make PREFIX=/path/to/OPENBLAS install步骤 5 执行以下命令设置OpenBLAS的环境变量。                    export LD_LIBRARY_PATH=/path/to/OPENBLAS/lib:$LD_LIBRARY_PATH----结束1.5 获取源码操作步骤步骤 1 下载QUANTUM ESPRESSO安装包。下载地址:https://github.com/QEF/q-e/releases/tag/qe-6.8步骤 2 使用SFTP工具将QUANTUM ESPRESSO安装包上传至服务器“/path/to/qe”目录。----结束1.6 编译和安装操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令进入主程序安装目录。                    cd /path/to/qe步骤 3 执行以下命令解压安装包。                    tar -zxvf q-e-qe-6.8.tar步骤 4 执行以下命令进入解压后路径。                    cd q-e-qe-6.8/步骤 5 执行以下命令开始编译。                    ./configure --with-cuda=yes --with-cuda-runtime=11.4 --with-cuda-cc=80  --enable-openmp --with-scalapack=no“--with-cuda-cc=80”请根据当前机器的显卡架构进行修改,此处测试机器显卡是v100。                    make -j32 pwall若编译机器无法联网,需下载“devicexlib-master.tar.gz”包,并上传至编译机器,编译完成后执行make pp -j32,具体命令如下:                    mv devicexlib-master.tar.gz /path/to/qe-gpu-6.7/external devicexlib.tar.gz                    tar xzf devicexlib.tar.gz --strip-components=1                    export F90FLAGS="-FAST -mCACHE_align -Mpreprocess -Mlarge_arrays"                    ./configure FC=pgf90 CC=pgcc --with-cuda=no --with-cuda-cc= --with-cuda-runtime= --disable-parallel --enable-cuda-env-check=no                    make pp -j32若出现类似如下错误:是由于链接库配置不对,需要更改“inc”文件。   打开“inc”文件。                    vi make.inc按“i”进入编辑模式,在如下位置增加“LD_LIBS = -lcurand”。按“Esc”键,输入:wq!,按“Enter”保存并退出编辑。步骤 6 执行以下命令查看是否生成可执行文件。                    ll /path/to/qe-gpu-6.7/bin/pw.x回显信息中包含“pw.x”文件,表示已生成可执行文件。步骤 7 执行以下命令设置环境变量。                    export PATH=/path/to/qe/bin/:$PATH----结束1.7 运行和验证操作步骤步骤 1 使用PuTTY工具,以root用户登录服务器。步骤 2 执行以下命令进入测试目录。                    cd /path/to/CASE步骤 3 执行以下命令下载测试算例,使用SFTP工具将测试算例上传至服务器“/path/to/CASE”目录。                    wget https://github.com/QEF/benchmarks/archive/refs/tags/bench0.0.tar.gz步骤 4 执行以下命令解压测试算例包,并将需要的文件拷贝到“/path/to/CASE”目录。                    tar xvf benchmarks-bench0.0.tar.gz                    cp benchmarks-master/AUSURF112/* ./步骤 5 执行以下命令运行QE程序。                    mpirun --allow-run-as-root -np 2 -x OMP_NUM_THREADS=1 --mca btl ^openib pw.x -input ./ausurf.in 2>&1 | tee -a qe.log“-np”表示测试使用的总进程数,这里写2是测试机器有2张显卡。“-x OMP_NUM_THREADS=1”表示指定每个进程的线程数为1。需要查看“qe.log”日志中的“WALL”数值,单位是“s”,数值越高性能越低,输出的结果如下图所示。----结束
  • [HPC] fastp 移植指南(CentOS 7.6)
    1 介绍fastp是一种工具,旨在为FastQ文件提供快速的多合一预处理。该工具是用C ++开发的,支持多线程以提供高性能。关于fastp的更多信息请访问GitHub相关页面。语言:C/C++一句话描述:超快速的多合一FASTQ预处理器。开源协议:MIT License建议的版本建议使用版本为“fastp 0.20.1”。2 环境要求硬件要求硬件要求如表2-1所示。表2-1 硬件要求项目说明CPUKunpeng 920 软件要求软件要求如表2-2所示。表2-2 软件要求项目版本下载地址fastp0.20.1https://github.com/OpenGene/fastp/archive/v0.20.1.tar.gz测试算例1ERR1044518_1.fastqftp://ftp.sra.ebi.ac.uk/vol1/fastq/ERR104/008/ERR1044518/ERR1044518_1.fastq.gz测试算例2ERR1044518_2.fastqftp://ftp.sra.ebi.ac.uk/vol1/fastq/ERR104/008/ERR1044518/ERR1044518_2.fastq.gz 操作系统要求操作系统要求如表2-3所示。表2-3 操作系统要求项目版本下载地址CentOS7.6https://www.centos.org/download/Kernel4.14.0-115.el7a.0.1https://www.centos.org/download/3 移植规划数据本章节给出fastp软件在移植过程中涉及到的相关软件安装规划路径的用途及详细说明。表3-1 移植规划数据序号软件安装规划路径用途说明1-基础环境搭建中的各安装包安装路径。参考《HPC解决方案 基础环境搭建指南》中“安装规划数据”章节。2/path/to/FASTPfastp的安装规划路径。这里的安装规划路径只是一个举例说明,建议部署在共享路径中。现网需要根据实际情况调整,后续章节凡是遇到安装路径的命令,都以现网实际规划的安装路径为准进行替换,不再单独说明。4 配置编译环境前提条件使用SFTP工具将各安装包上传至服务器对应目录下。配置流程表4-1 配置流程序号配置项说明1基础环境搭建参考《HPC解决方案 基础环境搭建指南》中“集群场景环境搭建”章节。5 获取源码操作步骤步骤 1    下载fastp源码包“fastp-0.20.1.tar.gz”。下载地址:https://github.com/OpenGene/fastp/archive/v0.20.1.tar.gz步骤 2    使用SFTP工具将下载好的软件包上传至服务器“/path/to/FASTP”目录。----结束6 编译和安装操作步骤步骤 1    使用PuTTY工具,以root用户登录服务器。步骤 2    执行以下命令,解压fastp安装包。tar -xvf fastp-0.20.1.tar.gz步骤 3    执行以下命令,进入解压后的目录。cd fastp-0.20.1步骤 4    执行以下命令,编译安装。make -j 16 步骤 5    执行以下命令,设置环境变量。export PATH=/path/to/FASTP/fastp-0.20.1:$PATH----结束7 运行和验证操作步骤步骤 1    使用PuTTY工具,以root用户登录服务器。步骤 2    执行以下命令进入算例目录。cd /path/to/FASTP/testdata步骤 3    执行以下命令解压算例文件。gzip -d ERR1044518_1.fastq.gzgzip -d ERR1044518_2.fastq.gz步骤 4    执行以下命令,运行算例。{ time fastp -i ERR1044518_1.fastq -I ERR1044518_2.fastq -w 16 ; } 2>&1 |tee fastp.log运行完成后,将回显以下信息:    Duplication rate: 0.581538%    Insert size peak (evaluated by paired-end reads): 169    JSON report: fastp.json    HTML report: fastp.html说明:    fastp支持多线程运行,-w指定线程数,但最多使用16个线程。运行完成后会生成fast.json和fast.html两份报告,可以通过-j和-h参数分别指定两个文件的路径和名称。更多参数信息可以使用fastp --help命令查看。----结束8 更多资源获取更多资源,请访问fastp的GitHub页面:https://github.com/OpenGene/fastp。
  • [交流吐槽] 常见的对比
    Runnable VS CallableCallable仅在 Java 1.5 中引入,目的就是为了来处理Runnable不支持的用例。Callable 接口可以返回结果或抛出检查异常Runnable 接口不会返回结果或抛出检查异常,如果任务不需要返回结果或抛出异常推荐使用 Runnable接口,这样代码看起来会更加简洁工具类 Executors 可以实现 Runnable 对象和 Callable 对象之间的相互转换。(Executors.callable(Runnable task)或 Executors.callable(Runnable task,Object resule))shutdown() VS shutdownNow()shutdown() :关闭线程池,线程池的状态变为 SHUTDOWN。线程池不再接受新任务了,但是队列里的任务得执行完毕。shutdownNow() :关闭线程池,线程的状态变为 STOP。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。 shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终isTerminated() VS isShutdown()isShutDown 当调用 shutdown() 方法后返回为 true。isTerminated 当调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true
  • [数据库] Oracle并行查询计划分析
    Oracle并行查询计划分析Oracle 的并行执行计划是由一个或者几个Data Flow Operator(DFO)组成,同一棵DFO树中,最多只有两组PX进程(生产者和消费者)。每个生产者进程都存在一个和每个消费者进程的连接,每个PX进程和QC都存在一个连接。两组PX进程之间,同一时间只存在一个活跃的数据分发,如果执行路径很长,数据需要多次分发,两组PX进程会变换生产者消费者角色,相互协作,完成所有并行操作PX进程之间或者与QC的连接至少存在一个(单节点下至多三个,RAC环境下至多四个)消息缓冲区用于进程间数据交互,该消息缓冲区默认在Largepool中分配(如果没有配置Largepool则在Sharedpool中分配)。多个缓冲区是为了实现异步通信,提高性能 Oracle并行执行计划树红色为数据消费者,蓝色为数据生产者数据消费者和数据生产之间的数据分发数据生产者和消费者之间数据分发有四种方式:send hashsend broadcastSend replicate(12c特性)adaptive(12 c新特性) send hash:生产者PX进程会根据join key,生成一个hash值,然后根据这个hash值把这行数据发送给一个消费者px进程, Hash分发的本质是把hashjoin的左边和右边(两个数据源),通过同样hash函数重新分发,切 分为N个工作单元(假设DoP=N),再进行join ,目的是减少PX进程进行join 操作时需要连接的数据量,如果hash join有一边在连接键上做hash分区,那么优化器可以选择对分区表不分发,因为hash分区已经对数据完成切分,这只需要hash分发hash join的其中一边,这就是partial partition wise join如果hash join的两边都在连接键上做了hash join分区,那么每个PX进程可以独立的处理对等的hash分区, 没有数据需要分发,这是full partition wise join send broadcast:广播分发是数据生产者将数据广播给所有的数据消费者,也就是说每个消费者都有一份数据,广播分发适合扫描后结果集相对比较小的表,广播分发是,数据结果行数*PX进程数据(消费者),相当于每个PX消费者进程都有一份相同的数据,小表驱动大表,建议做广播分发,如果分发的是大表,分发会消耗大量的CPU和内存Send replicate(12c特性):复制分发其实没有数据分发,每个PX进程重复扫描维度表(inner table),并行扫描事实表(outer table),在进行hash join时,每个PX进程重复扫描hashj oin 的左边的表,buffer cache 被用来缓存hashj oin 左边的小表,减少重复扫描所需的物理读,replicate 仅限于hashj oin 左边是表的情况,如果 hashj oin 的左边的结果集来自其他操作,比如join 或者视图,那么此时无法使用replicateadaptive(12 c新特性):通常, replicate 或者 broadcast 分发不受数据倾斜的影响.对于 hash 分发, hash join 两边连接键的最热门数据, 会被分发到同一 PX 进程进行 join 操作, 容易造成明显的并行执行倾斜.12c 引入 adaptive 分发, 可以解决 hash 分发时并行执行倾斜的问题.如果执行计划显示px send hybrid hash,说明该执行计划启用了adaptive分发特性。对hash join左边分发之前, 会插入一个STATISTICS COLLECTOR操作, 用于运行时确定hash join左边数据集的大小. 如果hash join左边的数据量小于并行度的两倍, 那么对于hash join左边的分发会切换为broadcast方式, 对hash join右边的分发为round-robin. 如果hash join左边的数据大于等于并行度的两倍, 对于hash join两边的分发方式都为hash, 和传统的hash分发一样如果存在柱状图信息, 表明hash join右边连接键上存在数据倾斜, 大部分数据为少数热门的值. 硬解析时, 会对hash join右边的表进行动态采样, 确认热点数据(重复度高的数据),Hash join的左边,的热点的数据会被广播到每个接收者,非热点数据被hash分发;Hash join右边的热点数据通过round-robin的方式发送, 非热点数据被hash分发. 布隆过滤:Oracle的布隆过滤器就是在执行join之前,过滤掉大部分的数据,其实就是包hash join的部分连接操作提前了,对 hash join 右边扫描时, 就第一时间把不符合 join条件的大部分数据过滤掉. 大大降低后续数据分发和 hash join 操作的成本. 不同的分布方式, 布隆过滤的生成和使用方式稍有不同:对于 broadcast 分发和 replicate, 每个 PX 进程持有 hash join 左边的完整数据, 对连接键生成 一个完整的布隆过滤, 扫描 hash join 右边时使用. 如果 sql 涉及多个维度表, 维度表全部使用broadcast 分发, 优化器可能对不同的维度表数据生成多个的布隆过滤, 在扫描事实表时同时使用.对于hash 分发,作为消费者的进程接收了hash  join左边的数据之后,每个PX进程分别对各自的数据集生成布隆过滤,在广播给作为生产者的每个PX进程,扫描右边数据使用。Oracle数据库会根据统计信息和SQL的过滤条件自动选择是否布隆过滤。大部分场景,使用布隆过滤都会提升性能,但在某些场景下,不建议使用布隆过滤,例如:hash join左边的结果集比较大,几百万或者几千万的数据,且连接键的重复数据不多,这种情况下使用布隆过滤,生成额布隆过滤会非常大,无法在CPU cache中完整缓存数据,没事使用布隆过滤都需要到内存里读取数据进行判断,会导致性能下降
  • [技术干货] Redis使用单线程却快到飞起的原因,全在这里了[转载]
    Redis想必大家都或多或少听过吧,我们在工作学习中通常用它来作为缓存使用,既然是作为缓存,大家的第一反应肯定是:这家伙很快!实际上它确实也很快 : ),但Redis底层却是单线程的!有同学可能就要有疑问了,为什么单线程的Redis却能够快到飞起?别急,我尽量用通俗易懂的语言来给各位说道说道~~Redis是单线程,主要是指Redis的网络IO和读写是由一个线程来完成的,但Redis的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。这不是本文讨论的重点,有个印象即可Redis为什么用单线程?多线程的开销通常情况下,在采用多线程后,如果没有良好的系统设计,其实是左图所展示的那样(注意纵坐标)。刚开始增加线程数时,系统吞吐率会增加,再进一步增加线程时,系统吞吐率就增长迟缓了,甚至还会出现下降的情况。上面两张图的标题手误被我标反了,源码还删了=_=关键瓶颈在于: 系统中通常会存在会被多线程同时访问的共享资源,为了保证共享资源的正确性,就需要有额外的机制保证线程安全性,例如加锁,这会带来额外的开销。比如拿最常用的List类型来举例吧,假设Redis采用多线程设计,有两个线程A和B分别对List做LPUSH和LPUSH操作,为了使得每次执行都是相同的结果,即【B线程取出A线程放入的数据】就需要让这两个过程串行执行。这就是多线程编程模式面临的共享资源的并发访问控制问题。并发访问控制一直是多线程开发中的一个难点问题:如果只是简单地采用一个互斥锁,就会出现即使增加了线程,大部分线程也在等待获取互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。同时加入并发访问控制后也会降低系统代码的可读性和可维护性,所以Redis干脆直接采用了单线程模式。Redis使用单线程为什么还这么快?之所以使用单线程是Redis设计者多方面衡量的结果。Redis的大部分操作在内存上完成采用了高效的数据结构,例如哈希表和跳表采用了多路复用机制,使其在网络IO操作中能并发处理大量的客户端请求,实现高吞吐率既然Redis使用单线程进行IO,如果线程被阻塞了就无法进行多路复用了,所以不难想象,Redis肯定还针对网络和IO操作的潜在阻塞点进行了设计。网络与IO操作的潜在阻塞点在网络通信里,服务器为了处理一个Get请求,需要监听客户端请求(bind/listen),和客户端建立连接(accept),从socket中读取请求(recv),解析客户端发送请求(parse),最后给客户端返回结果(send)。最基本的一种单线程实现是依次执行上面的操作。上面标红的accept和recv操作都是潜在的阻塞点:当Redis监听到有连接请求,但却一直不能成功建立起连接时,就会阻塞在accept()函数这里,其他客户端此时也无法和Redis建立连接当Redis通过recv()从一个客户端读取数据时,如果数据一直没有到达,也会一直阻塞基于多路复用的高性能IO模型为了解决IO中的阻塞问题,Redis采用了Linux的IO多路复用机制,该机制允许内核中,同时存在多个监听套接字和已连接套接字(select/epoll)。内核会一直监听这些套接字上的连接或数据请求。一旦有请求到达,就会交给Redis处理,这就实现了一个Redis线程处理多个IO流的效果。此时,Redis线程就不会阻塞在某一个特定的客户端请求处理上,所以它可以同时和多个客户端连接并处理请求。回调机制select/epoll一旦监测到FD上有请求到达时,就会触发相应的事件被放进一个队列里,Redis线程对该事件队列不断进行处理,所以就实现了基于事件的回调。例如,Redis会对Accept和Read事件注册accept和get回调函数。当Linux内核监听到有连接请求或读数据请求时,就会触发Accept事件和Read事件,此时,内核就会回调Redis相应的accept和get函数进行处理。Redis的性能瓶颈点经过上面的分析,虽然通过多路复用机制可以同时监听多个客户端的请求,但Redis仍然有一些性能瓶颈点,这也是我们平时编程需要极力避免的情况。1. 耗时操作任意一个请求在Redis中一旦耗时较久,都会影响整个server的性能。后面的请求都要等前面这个耗时请求处理完成,自己才能被处理到。这一点需要我们在设计业务场景时去规避;Redis的lazy-free机制也把释放内存的耗时操作放在了异步线程中去执行了。2. 高并发场景并发量非常大时,单线程读写客户端IO数据存在性能瓶颈,虽然采用IO多路复用机制,但还是只能单线程依次读取客户端的数据,无法利用到CPU多核。Redis在6.0可以利用CPU多核多线程读写客户端数据,但只是针对客户端的读写是并行的,每个命令的真正操作还是单线程。其他Redis相关的有趣问题借此机会也提几个和redis相关的有意思的问题。1. 为什么要用Redis,直接访问内存不好吗?这一条其实并没有很明确的界定,对于一些不经常变动的数据,可以直接放到内存里,不一定要放到Redis里,可以放到内存里。一致性问题:如果一个数据被修改了,数据在本地内存里的话,可能只有一台服务器上的数据被修改了。如果用Redis里面的话,我们访问Redis服务器,可以解决一致性问题。2. 数据太多内存放不下怎么办?比如我要缓存100G的数据,怎么办?这里也要打一个广告Tair是淘宝开源的分布式KV缓存系统,它从Redis继承了丰富的操作,理论上总数据量无限制,针对可用性、可扩展性、可靠性也进行了升级,感兴趣的小伙伴们可以了解一下~原文链接:https://blog.csdn.net/HNU_Csee_wjw/article/details/122567260
  • [技术干货] synchronize偏向锁底层实现原理[转载]
    链接:https://bbs.huaweicloud.com/blogs/3359231 偏向锁的意义无多线程竞争时,减少不必要的轻量级锁执行路径。大多数情况下,锁不仅不存在多线程竞争,而且总是由同一条线程去多次获得锁,为了让线程获得锁的性能代价更低而引入了偏向锁。偏向锁主要用来优化同一线程多次申请同一个锁的竞争,即当对象被当做同步锁并有一个线程抢到了锁时,则在Mark Word设置该线程的线程ID、是否偏向锁设置1、锁标志位设置01等信息,此时的Mark Word 存储的就是偏向锁状态信息。在:创建一个线程并在线程中执行循环监听的场景下或单线程操作一个线程安全集合时同一线程每次都需获取和释放锁,每次操作都会发生用户态与内核态的切换。获取偏向锁的场景:在自己的线程栈生成一条Lock Record,然后Object Reference指向对象头,此时Lock Record与对象头就建立了联系:① : 先判断Mard Word的Thread ID是否有值没有,则表示当前资源没有被其他线程占用,把当前线程ID等信息记录到Mark Word(这需CAS,可能多条线程修改Mark Word,需要保证原子性)有,则表示当前资源被线程占用,需要判断该线程是不是自己该线程ID是自己的,则表示可重入,直接获取(此时在自己的线程栈中继续生成一条新的Lock Record)该线程ID不是自己的,说明出现其他线程竞争,当前持有偏向锁的线程就需要撤销了,即当其他线程尝试获取偏向锁才释放锁轻量级锁的获取及释放依赖多次的CAS操作,而偏向锁只依赖一次CAS置换ThreadID。一旦出现多个线程竞争时必须撤销偏向锁,所以:撤销偏向锁消耗的性能必须 < 之前节省下来的CAS原子操作的性能消耗不然得不偿失!JDK6默认开启偏向锁,可通过-XX:-UseBiasedLocking禁用偏向锁。2 偏向锁的获取偏向锁的入口,synchronizer.cpp 文件的ObjectSynchronizer::fast_enter由BiasedLocking::revoke_and_rebias实现2.1 markOop mark = obj->mark()获取对象的markOop数据mark,即对象头的Mark Word2.2 判断mark是否为可偏向状态mark的偏向锁的锁标志位为 012.3 判断mark中JavaThread的状态若指向当前线程,则执行同步代码块若为空,则走4若指向其它线程,则走52.4 执行CAS原子指令设置mark中JavaThread为当前线程ID。若CAS成功,则执行同步代码块,否则走5。2.5 执行CAS失败说明当前存在多个线程竞争锁,当达到全局安全点(safepoint),获得偏向锁的线程就会被挂起,撤销偏向锁,并升级为轻量级锁。升级完成后被阻塞在安全点的线程继续执行同步代码块。BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) { assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint"); // We can revoke the biases of anonymously-biased objects // efficiently enough that we should not cause these revocations to // update the heuristics because doing so may cause unwanted bulk // revocations (which are expensive) to occur. // step1 markOop mark = obj->mark(); if (mark->is_biased_anonymously() && !attempt_rebias) { // We are probably trying to revoke the bias of this object due to // an identity hash code computation. Try to revoke the bias // without a safepoint. This is possible if we can successfully // compare-and-exchange an unbiased header into the mark word of // the object, meaning that no other thread has raced to acquire // the bias of the object. markOop biased_value = mark; markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark); if (res_mark == biased_value) { return BIAS_REVOKED; } } else if (mark->has_bias_pattern()) { Klass* k = obj->klass(); markOop prototype_header = k->prototype_header(); if (!prototype_header->has_bias_pattern()) { // This object has a stale bias from before the bulk revocation // for this data type occurred. It's pointless to update the // heuristics at this point so simply update the header with a // CAS. If we fail this race, the object's bias has been revoked // by another thread so we simply return and let the caller deal // with it. markOop biased_value = mark; markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark); assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked"); return BIAS_REVOKED; } else if (prototype_header->bias_epoch() != mark->bias_epoch()) { // The epoch of this biasing has expired indicating that the // object is effectively unbiased. Depending on whether we need // to rebias or revoke the bias of this object we can do it // efficiently enough with a CAS that we shouldn't update the // heuristics. This is normally done in the assembly code but we // can reach this point due to various points in the runtime // needing to revoke biases. if (attempt_rebias) { assert(THREAD->is_Java_thread(), ""); markOop biased_value = mark; markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch()); markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark); if (res_mark == biased_value) { return BIAS_REVOKED_AND_REBIASED; } } else { markOop biased_value = mark; markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark); if (res_mark == biased_value) { return BIAS_REVOKED; } } } } HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias); if (heuristics == HR_NOT_BIASED) { return NOT_BIASED; } else if (heuristics == HR_SINGLE_REVOKE) { Klass *k = obj->klass(); markOop prototype_header = k->prototype_header(); if (mark->biased_locker() == THREAD && prototype_header->bias_epoch() == mark->bias_epoch()) { // A thread is trying to revoke the bias of an object biased // toward it, again likely due to an identity hash code // computation. We can again avoid a safepoint in this case // since we are only going to walk our own stack. There are no // races with revocations occurring in other threads because we // reach no safepoints in the revocation path. // Also check the epoch because even if threads match, another thread // can come in with a CAS to steal the bias of an object that has a // stale epoch. ResourceMark rm; if (TraceBiasedLocking) { tty->print_cr("Revoking bias by walking my own stack:"); } EventBiasedLockSelfRevocation event; BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL); ((JavaThread*) THREAD)->set_cached_monitor_info(NULL); assert(cond == BIAS_REVOKED, "why not?"); if (event.should_commit()) { event.set_lockClass(k); event.commit(); } return cond; } else { EventBiasedLockRevocation event; VM_RevokeBias revoke(&obj, (JavaThread*) THREAD); VMThread::execute(&revoke); if (event.should_commit() && (revoke.status_code() != NOT_BIASED)) { event.set_lockClass(k); // Subtract 1 to match the id of events committed inside the safepoint event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1); event.set_previousOwner(revoke.biased_locker()); event.commit(); } return revoke.status_code(); } } assert((heuristics == HR_BULK_REVOKE) || (heuristics == HR_BULK_REBIAS), "?"); EventBiasedLockClassRevocation event; VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD, (heuristics == HR_BULK_REBIAS), attempt_rebias); VMThread::execute(&bulk_revoke); if (event.should_commit()) { event.set_revokedClass(obj->klass()); event.set_disableBiasing((heuristics != HR_BULK_REBIAS)); // Subtract 1 to match the id of events committed inside the safepoint event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1); event.commit(); } return bulk_revoke.status_code(); }3 偏向锁的撤销只有当其它线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销由BiasedLocking::revoke_at_safepoint实现:void BiasedLocking::revoke_at_safepoint(Handle h_obj) { assert(SafepointSynchronize::is_at_safepoint(), "must only be called at safepoint"); oop obj = h_obj(); HeuristicsResult heuristics = update_heuristics(obj, false); if (heuristics == HR_SINGLE_REVOKE) { revoke_bias(obj, false, false, NULL, NULL); } else if ((heuristics == HR_BULK_REBIAS) || (heuristics == HR_BULK_REVOKE)) { bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL); } clean_up_cached_monitor_info(); }偏向锁的撤销动作必须等待全局安全点(safepoint,GC时会让所有线程阻塞的停顿点)暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态撤销偏向锁,恢复到无锁(标志位 01)或轻量级锁(标志位 00)状态偏向锁在Java 1.6后默认启用,但在应用程序启动几s后才激活,可关闭延迟:-XX:BiasedLockingStartupDelay=0若确定应用程序中所有锁通常情况下处于竞争状态,可关闭偏向锁:XX:-UseBiasedLocking=false(默认打开)偏向锁的释放遍历线程栈的所有Lock Record,把ObjectReference切断,即ObjectReference = null.把ObjectReference置null,但锁对象的对象头的Mark Word还是没改变,依然偏向之前的线程,那还是没释放锁的嘛,的确是,线程退出临界区时候,并没有释放偏向锁,这么做是为 : 当再次需要获取锁时,只需要简单判断是否是重入,即可快速获取锁,而不用每次都CAS,这也是偏向锁在只有一个线程访问锁的情景下高效的核心。总结当出现锁资源访问的时候,都会在当前线程栈生成一条Lock Record,并且ObjectReference将指向锁对象的对象头 的Mark Word,该设置可能出现多线程,需CAS操作多线程情况下竞争同一个锁资源,偏向锁的撤销会影响效率偏向锁的重入计数依靠线程栈里Lock Record个数偏向锁撤销失败,最终会升级为轻量级锁偏向锁退出时并没有修改Mark Word,也就是没有释放锁偏向锁相对轻量级锁来说,当同一线程去再次获取锁的时候,不用进行CAS操作,提高了性能.(轻量级锁在同一线程情况下每次去获取锁,在无锁的状态下,每次都要进行一次CAS操作)偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁偏向锁的撤销是很复杂,成为理解代码的障碍,也阻碍了对同步系统重构,而且现如今基本都是多核系统,偏向锁的劣势越来越明显,所以在Java 15废弃了偏向锁】
  • [技术干货] 【故障注入第十七期】Linux OS相关故障-进程
    今天周五啦,掐指一算,好久没更新帖子了,今天继续我们上一期的话题,使用DemonCAT,如何注入Linux OS进程方面的故障呢,我们一起来看下吧!进程异常退出进程/线程死循环 进程/线程D状态进程挂起线程挂起进程Z状态线程数过多进程句柄数耗尽线程异常退出进程数过多进程页表数据被踩进程内存泄漏(分配)内核线程R死锁内核进程死锁进程内存数据被踩停止进程定时器进程内存泄漏(malloc)DemonCAT关于Linux OS 注入进程故障方面的内容就先介绍到这,当然如果有关于任何章节感兴趣的伙伴,欢迎留言或联系我们,下一期,向大家介绍文件系统方面的故障内容,尽情期待~
  • [问题求助] 关于多线程、多device提升推理性能的一些疑惑
    1、对于单device来说(例如运行设备是Atlas 200DK),使用多线程提升推理性能是将预处理、推理、后处理放在不同的线程中执行,对于多device来说,提升推理性能是将预处理、推理、后处理放在同一个线程中,然后起多路线程,每个线程在不同的设备上运行。不知道我这样理解对吗?如果说按照上述的多个device起多个线程执行一个模型的预处理、推理、后处理是不是意味着可以调用多个device的算力和资源,使得推理性能翻倍?2、对于gitee中samples仓中c++多线程多device的样例,例如cplusplus/level2_simple_inference/n_performance/1_multi_process_thread/animeGAN_multi_device_one_video · Ascend/samples - 码云 - 开源中国 (gitee.com)目前我的运行设备是atlas200DK,属于单device,如果想实现多device提升推理性能,不太清楚多块Atlas200DK如何连接来实现多device的目的。
总条数:756 到第
上滑加载中