1. 什么叫做CPU密集型(CPU-bound)
- 把CPU运算单元跑满
- 把CPU控制器跑满
2. CPU密集型的表现出来是怎么样的?
CPU利用率高(不包括IO导致的CPU wait)
3. CPU指令集大概有哪些?
- 把运算单元(ALU)跑满:add/sub/AND/OR/slt (算术运算、逻辑运算、移位操作等指令)
- 【数据传输】访问存储器指令利用ALU计算地址
- 【算术指令】算术逻辑计算指令用ALU执行运算
- 【条件分支】分支指令用ALU进行比较,然后决定是否改变下一条指令地址
- 【无条件分支】直接跳转
- 把控制器跑满: 单位时间内大量的取指令、分析指令
4. CPU密集型的应用程序代码又是如何的呢?
- 把运算单元(ALU)跑满(与上面对应)
- 【数据传输】取数据,比如访问数组、集合类
- 【算术指令】赋值运算
- 【条件分支】if语句和循环语句
- 【无条件分支】方法调用、返回、case/switch语句
- 把控制器跑满: 单位时间内大量的取指令、分析指令。试试while死循环
5. 单线程让CPU跑满的代码
package com.tinygao.thread.runcpu;
import com.google.common.base.*;
import com.google.common.primitives.Booleans;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
/**
* Created by gsd on 2017/1/15.
*/
@Slf4j
public class SingleRunCpu {
public static long time = 0;
public static void main(String[] args) {
Preconditions.checkNotNull(args, "输入不能为空");
Preconditions.checkState(Double.parseDouble(args[0]) > 0,
"输入要是double类型的,且不能小于0");
Preconditions.checkState(args.length==2, "有两个参数");
final RateLimiter rateLimiter = RateLimiter.create(Double.parseDouble(args[0]));
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
log.info("time : " + time);
}
});
while(time < 1000000000000L) {
time++;
if(Boolean.parseBoolean(args[1])) {
rateLimiter.acquire(1);
}
}
}
}
- 运行脚本: java -Djava.ext.dirs=/data/storage/thread/threadCourse -cp . com.tinygao.thread.runcpu.SingleRunCpu $1 $2
- 根据第二个参数来控制,是否使用流控。true:使用,flase:不使用。用户可以通过调控流控来减少指令的执行。
-
<b>所有的jar包放在threadCourse目录下</b>
2.6 多线程让CPU跑满的代码
package com.tinygao.thread.runcpu;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Created by gsd on 2017/1/15.
*/
@Slf4j
public class MultiRunCpu {
public static long maxCount = 1L;
public static int threadNum = 1;
public static void main(String[] args) throws InterruptedException {
Preconditions.checkNotNull(args, "输入不能为空");
Preconditions.checkState((maxCount = Long.parseLong(args[0])) > 0,
"输入的运行的,且不能小于0");
Preconditions.checkState((threadNum = Integer.parseInt(args[1])) > 0,
"输入的线程数必须是大于0的");
Stopwatch sw = Stopwatch.createStarted();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
log.info("waster time : {} s" ,
sw.elapsed(TimeUnit.SECONDS)
);
sw.stop();
}
});
ExecutorService runners = Executors.newFixedThreadPool(threadNum);
for(int i = 0 ;i < threadNum; i++) {
runners.submit(MultiRunCpu::run);
}
runners.shutdown();
runners.awaitTermination(1,TimeUnit.DAYS);
}
private static void run() {
long threadCount = (maxCount/threadNum) + 1;
long count = 0;
while(count < threadCount) {
count++;
}
}
}
- ./runMultiCpu.sh 1000000000 1
-
java -Djava.ext.dirs=/data/storage/thread/threadCourse -cp . com.tinygao.thread.runcpu.MultiRunCpu $1 $2
<b> 测试机器的CPU是4核8线程。</b>
- CPU利用率100% ,线程数配置在4后达到最大吞吐量,即使加到16个依然是29s完成。
-
线程升到6400个后,特别是32000个,性能反而下降了。这时候vmstat发现in和cs数上去了,中断次数和上下文切换次数变多。
6. CPU性能
CPU性能 = 指令数 × CPI × 时钟周期时间
- 时钟周期依赖你购买的CPU
- CPI : 每条指令的平均时钟周期数,也就是每条指令所需要多少个时钟周期
- 指令数: 应用程序翻译成CPU指令个数
7. 提高性能
- 程序员可控的减少指令数和缩短程序CPI
- 指令(MIPS)具有5个处理步骤,不同步骤时间开销不一样
- 从指令存储器中读取指令
- 指令译码和读寄存器
- 执行操作或计算地址
- 从数据存储器中读取操作数
- 将结果写回寄存器
</br>
涉及到存储器(包含cache)、寄存器、cpu计算单元、总线等。<b> 比较慢的应该是与内存打交道的指令。</b> 如果说相同指令情况下,如果减少较慢的指令,应用程序的性能就会提升。
8 .问题
- 【在多线程】如果使用AtomicLong来代替上面的count,不做线程分配maxCount的数量,线程越多速度反而不如单线程的速度(作为遗留问题)。
- 【在多线程】4核8线程CPU,应用程序4个线程时候吞吐量最高,而不是8个线程。硬件核会模拟出一个虚拟核(线程),要想一个核中两个线程要想同时进行,指令必须是不一样的。如果一样(都是add),则效果跟单核一样。