Java ForkJoinPool与普通递归
在阅读了有关的内容后,我尝试了一个实验来测试与普通递归相比,Java ForkJoinPool与普通递归,java,recursion,concurrency,forkjoinpool,Java,Recursion,Concurrency,Forkjoinpool,在阅读了有关的内容后,我尝试了一个实验来测试与普通递归相比,ForkJoinPool的实际速度有多快 我递归地计算了一个文件夹中的文件数,令我惊讶的是,普通递归比ForkJoinPool 这是我的密码 递归任务 class DirectoryTask extends RecursiveTask<Long> { private Directory directory; @Override protected Long compute() {
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;
}
}
class DirectoryTask扩展了RecursiveTask{
专用目录;
@凌驾
受保护的长计算(){
List forks=new ArrayList();
List directories=directory.getDirectories();
for(目录:目录){
DirectoryTask DirectoryTask=新建DirectoryTask(目录);
add(directoryTask);
directoryTask.fork();
}
Long count=directory.getDoumentCount();
for(递归任务:forks){
count+=task.join();
}
返回计数;
}
}
普通递归
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();
}
private static Long getFileCount(目录){
长递归计数=0L;
List directories=directory.getDirectories();
if(null!=目录){
用于(目录d:目录){
recursiveCount+=getFileCount(d);
}
}
返回recursiveCount+目录.getDoumentCount();
}
目录对象
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);
}
}
类目录{
私有列表目录;
专用长doumentCount=0L;
静态目录fromFolder(文件){
List children=new ArrayList();
长文档计数=0L;
如果(!file.isDirectory()){
抛出新的IllegalArgumentException(“只允许目录”);
}
String[]files=file.list();
if(null!=文件){
用于(字符串路径:文件){
文件f=新文件(File.getPath()+File.separator+路径);
if(f.ishiden()){
继续;
}
if(f.isDirectory()){
add(Directory.fromFolder(f));
}否则{
documentCount++;
}
}
}
返回新目录(子目录、文档计数);
}
}
结果
- 普通递归:3ms
- 游泳池:25毫秒
我只是想了解是否存在一个特定的阈值,低于这个阈值,普通递归比ForkJoinPool更快。生活中没有什么是免费的。如果你不得不把一箱啤酒从你的车里搬到你的公寓里,那么什么更快:用手把它运到那里,或者先到棚子里,让手推车用它来搬动那一箱 创建线程对象是一个“本机”操作,它深入底层操作系统以获取那里的资源。这可能是一个相当昂贵的操作 意思是:仅仅在一个问题上抛出“更多线程”并不会自动加快速度。恰恰相反。当您的任务主要是CPU密集型任务时,并行处理可能会带来很小的好处。当你做很多IO时,有多个线程可以让你总体上“少”等待;从而提高您的吞吐量 换句话说:Fork/Join在完成真正的工作之前需要大量的活动。将它用于只需要几毫秒的计算简直是小题大做。因此:您可能希望使用“fork/join”操作来处理更大的数据集
为了进一步阅读,您可以查看。在盖子下使用分叉/连接框架;令人惊讶的是,期望任意
并行流也比普通流“更快”是一种误解。这有多个方面:
对于同一个问题,串行(例如普通递归)和并行(例如forkjoin)解决方案之间是否存在差异
并行化文件系统访问的范围是什么
衡量绩效的陷阱是什么
对#1的答复。是的,有区别。对于太小的问题,并行性是不好的。对于并行解决方案,您需要考虑以下方面的日常开支:
- 创建和管理线程
- 将信息从父线程传递到子线程
- 将子线程的结果返回到父线程
- 对共享数据结构的同步访问
- 等待最慢/最后一个完成的子线程完成
这些在实践中如何发挥作用取决于多种因素。。。包括问题的规模和并行性的机会
#2的答案(可能)没有你想象的那么多。典型的文件系统存储在具有物理特性(如磁盘旋转和磁头查找)的磁盘驱动器上。这些通常会成为瓶颈,而且高端存储系统越少,并行性的空间就越小
#3的答案是有很多陷阱。这些陷阱可能会导致非常误导(即无效)的性能结果。。。。如果你不允许的话。最大的陷阱之一是JVM需要时间“预热”;i、 加载类,进行JIT编译,调整堆大小,等等
另一个适用于执行文件系统I/O的基准测试的陷阱是,典型的操作系统将执行诸如缓存磁盘块和文件/目录元数据之类的操作。因此,第二次访问文件或目录可能比第一次快
话虽如此,如果您有一个设计良好的高性能文件系统(例如SSD上的inode)和一个设计良好的应用程序,以及足够的内核,那么就有可能通过并行实现非凡的文件系统扫描速率。(例如,在半小时内检查修改时间戳