在IO绑定任务上使用CompletableFuture时的运行时差异

问题描述

我对JVM多线程模型的理解是,当线程执行IO调用时,该线程为BLOCKED,并由JVM / OS放入等待队列,直到数据可用为止。

我正在尝试在代码中模拟这种行为,并使用JMHCompletableFuture运行具有各种线程大小的基准测试。

但是,结果不是我期望的。我期望无论线程数量(具有内存限制)如何,执行时间都是恒定的(具有线程/上下文切换开销),因为任务是IO绑定的,而不是CPU绑定的。

Async IO results

我的cpu是4核/ 8线程笔记本电脑处理器,即使只有1或2个线程,预期的行为也存在差异。

我正在尝试在异步任务中读取5MB文件(每个线程一个单独的文件)。在每次迭代的开始,我创建一个具有所需线程数的FixedThreadPool

@Benchmark
public void readAsyncIO(Blackhole blackhole) throws ExecutionException,InterruptedException {
    List<CompletableFuture<Void>> readers = new ArrayList<>();

    for (int i =0; i< threadSize; i++) {
         int finalI = i;
         readers.add(CompletableFuture.runAsync(() -> readFile(finalI),threadPool));
    }

    Object result =  CompletableFuture
                     .allOf(readers.toArray(new CompletableFuture[0]))
                     .get();
    blackhole.consume(result);
}
@Setup(Level.Iteration)
public void setup() throws IOException {
    threadPool = Executors.newFixedThreadPool(threadSize);
}
@TearDown(Level.Iteration)
public void tearDown() {
    threadPool.shutdownNow();
}
public byte[] readFile(int i)  {
    try {
        File file = new File(filePath + "/" + fileName + i);
        byte[] bytesRead = new byte[(int)file.length()];
        InputStream inputStream = new FileInputStream(file);
        inputStream.read(bytesRead);
        return bytesRead;
    } catch (Exception e) {
        throw new CompletionException(e);
    }
}

还有JMH配置,

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 3)
@Fork(value=1)
@Measurement(iterations = 3)
public class SimpleTest {

    @Param({ "1","2","4","8","16","32","50","100" })
    public int threadSize;
    .....

}

关于我在做什么错的任何想法?还是我的假设不正确?

解决方法

这似乎是合理的。对于单线程,您会看到1个文件需要2毫秒来处理,添加更多线程将导致每个线程的平均时间更长,因为每个大容量的read(bytesRead)可能会读取多个磁盘,因此可能会有IO机会阻塞和线程上下文切换,再加上-取决于磁盘-更多的查找时间。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...