ForkJoinPool VS平原递归
阅读ForkJoinPool后,我尝试了一个实验来测试速度有多快实际上ForkJoinPool
是,相比于普通的递归时。ForkJoinPool VS平原递归
我计算出的文件数的文件夹中递归,和我surprize,普通递归执行的方式优于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;
}
}
平原递归
private static Long getFileCount(Directory directory) {
Long recursiveCount = 0L;
List<Directory> directories = directory.getDirectories();
if (null != directories) {
for (Directory d : directories) {
recursiveCount += getFileCount(d);
}
}
return recursiveCount + directory.getDoumentCount();
}
Directory对象
class Directory {
private List<Directory> directories;
private Long doumentCount = 0L;
static Directory fromFolder(File file) {
List<Directory> children = new ArrayList<>();
Long documentCount = 0L;
if (!file.isDirectory()) {
throw new IllegalArgumentException("Only directories are allowed");
}
String[] files = file.list();
if (null != files) {
for (String path : files) {
File f = new File(file.getPath() + File.separator + path);
if (f.isHidden()) {
continue;
}
if (f.isDirectory()) {
children.add(Directory.fromFolder(f));
} else {
documentCount++;
}
}
}
return new Directory(children, documentCount);
}
}
个结果
- 平原递归:3ms的
- ForkJoinPool:25ms的
在哪里,这里的错误?
我只是想了解是否有特定的阈值,低于该普通递归比ForkJoinPool更快。
生活中没有任何东西是免费的。如果你必须将一辆啤酒箱从你的车上搬到你的公寓 - 那么更快:手动将它搬到那里,或者先到棚屋去让独轮车使用它来移动那个箱子?
创建线程对象是一个“原生”的操作,下降到底层操作系统到那里获取资源。这可能是一个相当昂贵的操作。
含义:就在一个问题扔“更多的线程”没有自动加快速度。与此相反的。当你的任务主要是CPU密集型时,并行处理可能会带来小的收益。当你做很多的IO时,然后有多个线程可以让你做更少的整体等待;从而提高您的吞吐量。
换句话说:叉/加入需要大量的活动它的真正的工作之前。将它用于只需要几毫秒的计算简直就是矫枉过正。因此:你会期待在更大的数据集上工作的“fork/join”操作。
对于进一步的阅读,你可能看parallel streams。那些使用封面下的fork/join框架;令人惊讶的是,期望任意parallelStream
比普通流“更快”也是一种误解。
有多个方面,以这样的:
是否有串行(例如纯递归)之间并平行(例如forkjoin)解决方案,以同样的问题的差?
并行化文件系统访问的范围是什么?
什么是测量性能的陷阱?
回答#1。是,有一点不同。并行性对于太小的问题不好。随着并行解决方案,你需要考虑的开销:
- 创建和管理线程
- 从父线程孩子传递信息线程
- 从子线程父线程返回结果
- 对共享数据结构的同步访问,
- 等待最慢/最后完成子线程完成。
如何将这些在实践中发挥出依赖于各种各样的事情......包括问题的大小和并行性的机会。
#2的答案(可能)不像您想象的那么多。典型的文件系统存储在具有物理特性(例如磁盘旋转和磁盘头查找)的磁盘驱动器中。这些通常会成为瓶颈,而且您拥有高端存储系统的机会越来越少,并行性就没有太大的空间。
#3的答案是有很多陷阱。而且这些陷阱可能会导致非常误导(即无效)的性能结果......如果您不允许的话。最大的陷阱之一是JVM需要时间来“热身”;即加载类,执行JIT编译,调整堆大小等等。
另一个适用于执行文件系统I/O的基准测试的陷阱是典型的操作系统将执行缓存磁盘块和文件/目录元数据等操作。因此,第二次访问文件或目录时,它可能比第一次更快。
话虽如此,如果你有一个精心设计的,高性能文件系统(保持在SSD上如索引节点)和一个精心设计的应用程序,以及足够的核心,可以实现文件系统的非凡速度通过并行扫描。 (例如,在12小时内检查5亿个文件的修改时间戳....)
谢谢斯蒂芬:) –
非常感谢您的澄清。所以,我从这里的答案(Stephen C的一个), 当正在执行的任务实际需要一段时间才能完成时,ForkJoinPool是人为的,以补偿线程创建的开销。 –
正确。除此之外:你还必须小心如何/你的基准。如果您多次重复实验时执行时间发生变化,这并不会让我感到惊讶。 – GhostCat