在阅读了ForkJoinPool之后,我尝试了一个实验来测试实际l
与普通递归相比的速度。
我以递归方式计算了文件夹中的文件数量,而且令我惊讶的是,普通递归的执行方式优于%
这是我的代码。
递归任务
ForkJoinPool
普通递归
ForkJoinPool
目录对象
class DirectoryTask extends RecursiveTask<Long> {
private Directory directory;
@Override
protected Long compute() {
List<RecursiveTask<Long>> forks = new ArrayList<>();
List<Directory> directories = directory.getDirectories();
for (Directory directory : directories) {
DirectoryTask directoryTask = new DirectoryTask(directory);
forks.add(directoryTask);
directoryTask.fork();
}
Long count = directory.getDoumentCount();
for (RecursiveTask<Long> task : forks) {
count += task.join();
}
return count;
}
}
结果
这里的错误在哪里?
我只是想了解是否存在特定的阈值,低于该阈值,普通递归比ForkJoinPool更快。
答案 0 :(得分:5)
生命中没有任何东西是免费的。如果你不得不将一个啤酒箱从你的汽车移到你的公寓 - 更快的是:手动携带它,或者先去棚屋,让独轮车用它移动那个箱子?
创建线程对象是&#34; native&#34;进入底层操作系统以获取资源的操作。这可能是一项相当昂贵的操作。
含义:只是投掷&#34;更多线程&#34;在一个问题上并没有自动加快速度。与此相反的。当您的任务主要是CPU密集型时,并行执行操作可能会带来很小的收益。当你做很多IO时,那么拥有多个线程可以让你做更少的事情。整体等待;从而提高了吞吐量。
换句话说:Fork / Join在完成实际工作之前需要相当多的活动。使用它进行只需几毫秒的计算就简直太过分了。因此:你会期待&#34; fork / join&#34;适用于较大数据集的操作。
如需进一步阅读,您可以查看parallel streams。那些是在封面下使用fork / join框架;而且意外的是,期待任意parallelStream
更快&#34;是一种误解。也比普通的溪流。
答案 1 :(得分:5)
这有多个方面:
同一问题的串行(例如普通递归)和并行(例如forkjoin)解决方案之间是否存在差异?
并行化文件系统访问的范围是什么?
衡量效果的陷阱是什么?
回答#1。是,有一点不同。并行性对于太小的问题并不好。使用并行解决方案,您需要考虑以下开销:
这些在实践中如何发挥取决于各种各样的事情......包括问题的大小,以及并行的机会。
#2的回答(可能)没有您想象的那么多。典型的文件系统存储在具有物理特性(如磁盘旋转和磁头搜索)的磁盘驱动器上。这些通常会成为瓶颈,而且你拥有高端存储系统的可能性就越小,并行性就没有太大的余地了。
#3的答案是有很多陷阱。而这些陷阱可能导致非常误导(即无效)的表现结果....如果你不允许这些结果。其中一个最大的陷阱是JVM需要时间来预热&#34;即加载类,执行JIT编译,调整堆大小等等。
适用于执行文件系统I / O的基准测试的另一个陷阱是典型的操作系统将执行缓存磁盘块和文件/目录元数据之类的操作。因此,第二次访问文件或目录时,它可能比第一次更快。
话说回来,如果你有一个设计良好的高性能文件系统(例如SSD上的inode)和设计良好的应用程序,以及足够的内核,就可以通过并行性实现非凡的文件系统扫描率。 (例如,在12小时内检查5亿个文件的修改时间戳....)