2015-06-07 71 views
3

我想使用线程对文件进行排序。这里是Sort.java:螺纹排序运行速度比非线程排序慢

此功能排序与线程

public static String[] threadedSort(File[] files) throws IOException { 
     String sortedData[] = new String[0]; 
     int counter = 0; 
     boolean allThreadsTerminated = false; 
     SortingThread[] threadList = new SortingThread[files.length]; 
     for (File file : files) { 
      String[] data = getData(file); 
      threadList[counter] = new SortingThread(data); 
      threadList[counter].start(); 
      counter++; 
     } 
     while(!allThreadsTerminated) { 
      allThreadsTerminated = true; 
      for(counter=0; counter<files.length; counter++) { 
       if(threadList[counter].getState() != Thread.State.TERMINATED) { 
        allThreadsTerminated = false;    
       }   
      } 
     } 
     for(counter=0; counter<files.length; counter++) { 
      sortedData = MergeSort.merge(sortedData, threadList[counter].data); 
     } 
     return sortedData; 
} 

此功能只是各种正常的帮助,现在

public static String[] sort(File[] files) throws IOException { 
    String[] sortedData = new String[0]; 
    for (File file : files) { 
     String[] data = getData(file); 
     data = MergeSort.mergeSort(data); 
     sortedData = MergeSort.merge(sortedData, data); 
    } 
    return sortedData; 
    } 

当我用那种左右逢源的正常排序是比螺纹快版。什么可能是它的原因?我错过了什么?

我SortingThread是这样的:

public class SortingThread extends Thread { 
    String[] data; 
    SortingThread(String[] data) { 
     this.data = data; 
    } 
    public void run() { 
     data = MergeSort.mergeSort(data);   
    } 
} 

当我分析一下我通过它的性能比较原始的非线程实现线程实现,我觉得第二个快。什么可能是这种行为的原因?如果我们谈论相对的性能改进,我们希望线程实现速度更快,如果没有错的话。

编辑:假设我有适当的功能MergeSort。但是没有必要在这里发布它的代码。另外getData()函数只是从文件中获取输入。 我认为问题在于我正在整个文件中的数组。我认为我应该提供不同的线,以不同的线程:

private static String[] getData(File file) throws IOException { 
    ArrayList<String> data = new ArrayList<String>(); 
    BufferedReader in = new BufferedReader(new FileReader(file)); 
    while (true) { 
     String line = in.readLine(); 
     if (line == null) { 
     break; 
     } 
     else { 
     data.add(line); 
     } 
    } 


    in.close(); 
    return data.toArray(new String[0]); 
    } 
+0

什么是您的计时数据?它有多快?或者用“噪音艺术”的话来说:“速度有多快?”您似乎正在整理文件内容。文件系统访问可能是瓶颈。创建线程是一个沉重的过程,但它可能没有任何好处。 –

+0

如果您需要执行操作并在最后结合结果,ForkJoinPool可能是更好的选择。 – xTrollxDudex

+0

@RogerGustavsson Sort.sort花费1.129517647秒读取和排序数据。 Sort.threadedSort花费3.171421661秒来读取和排序数据。 – ms8

回答

1

首先,你如何测量流逝的时间?你在同一个程序中执行两个测试吗?如果是这样,请记住mergesort可能会在第一次测试执行时进行热点编译。我建议你运行每种方法两次,测量第二次运行的时间

+0

你是对的!如果我首先调用普通的排序http://pastebin.com/j7nLAKkz,它的结果不同于我先通过线程排序http://pastebin.com/6BzCmmxn的结果。什么可能是它的原因?请帮助 – ms8

+0

另一个原因是当你运行第一次排序时文件被缓存,所以第二次排序*读取文件的速度更快*这可能比所有线程更重要:) – Michael

+0

@Michael如何从缓存中删除它然后?所以这两个执行都是独立的 – ms8

0

你有多少个CPU /内核?这段代码的一个问题是主线程在“while(!allThreadsT​​erminated)”循环中花费CPU时间,主动检查线程状态。如果你有一个CPU - 你正在浪费它,而不是实际的排序。

for(counter=0; counter<files.length; counter++) { 
     threadList[counter].join(); 
} 
+0

你的意思是用此语句替换while循环? – ms8

+0

我们可以为此程序提供一些更快的I/O启动程序吗? – ms8

+0

是的,while循环及其所有内容。 – Michael

0

您应该使用流和标准排序:

与更换while循环

static String[] sort(File[] files, boolean parallel) { 
    return (parallel ? Stream.of(files).parallel() : Stream.of(files)) 
     .flatMap(f -> { 
      try { 
       return Files.lines(f.toPath()); 
      } catch (Exception e) { 
       e.printStackTrace(); 
       return null; 
      } 
     }) 
     .sorted() 
     .toArray(String[]::new); 
} 

static String[] sort(File[] files) { 
    return sort(files, false); 
} 

static String[] threadSort(File[] files) { 
    return sort(files, true); 
} 

在我environmet threadSort更快。

sort: 
files=511 sorted lines=104419 elapse=4784ms 
threadSort: 
files=511 sorted lines=104419 elapse=3060ms 
+0

我只想使用我的两种方法。 – ms8

+0

我们可以为此程序提供一些更快的I/O启动程序吗? – ms8

+0

将I/O置于并行化之外比您更快一点。但是排序每个文件的速度较慢。 – saka1029

0

您可以使用java.util.concurrent.ExecutorService将在线程的指定数量的运行所有的任务,而一旦所有线程都执行完毕后,你会得到一个列表Future对象将举行每个线程执行的结果。未来对象列表将按照您将Callable对象插入列表中的顺序排列。

首先你需要的是让你的SortingThread实现Callable接口,这样你就可以得到每个线程执行的结果。
每个Callable对象必须实现call()方法,其返回类型将是您的Future对象。

public class SortingThread implements Callable<String[]> { 
    String[] data; 
    SortingThread(String[] data) { 
     this.data = data; 
    } 
    @Override 
    public String[] call() throws Exception { 
     data = MergeSort.mergeSort(data); 
     return data; 
    } 
    } 

接下来你需要的是使用ExecutorSerivce进行线程管理。

public static String[] sortingExampleWithMultiThreads(File[] files) throws IOException { 
     String sortedData[] = new String[0]; 
     int counter = 0; 
     boolean allThreadsTerminated = false; 
     SortingThread[] threadList = new SortingThread[files.length]; 
     ArrayList<Callable<String[]>> callableList = new ArrayList<Callable<String[]>>(); 
     for (File file : files) { 
      String[] data = getData(file); 
      callableList.add(new SortingThread(data)); //Prepare a Callable list which would be passed to invokeAll() method. 
      counter++; 
     } 

     ExecutorService service = Executors.newFixedThreadPool(counter); // Create a fixed size thread pool, one thread for each file processing... 
     List<Future<String[]>> futureObjects = service.invokeAll(callableList); //List of what call() method of SortingThread is returning... 

     for(counter=0; counter<files.length; counter++) { 
      sortedData = MergeSort.merge(sortedData, futureObjects.get(counter)); 
     } 
     return sortedData; 
} 

这样你能避免使用WHILE循环这是众所周知的提高CPU利用率(因此在速度降低),并且如果你有单核CPU然后它可以达到的利用率为100%,而如果双核心然后是50%。
另外,使用ExecutorService进行线程管理是处理多线程而不是dev启动和监视线程以获得结果的更好方法。 所以,你可以期待性能。

我还没有跑过它,所以你可能需要这样做,改变这里和那里,但我突出了你的方法。

P.S .:在衡量性能时,为了得到整洁和精确的结果,总是为每次运行创建一个新的JVM实例。

+0

我们可以为这个程序增加一些更快的I/O startergy吗? – ms8

+0

我们必须使用线程来完成工作,现在没有更快的线程和更慢的线程。所以,我可以建议的最好的事情是:1.使用Java自己的ExecutorService API,以便您期望最佳结果。 2.所有这些多线程API(如ExecutorService,ThreadPool等)都不利用CPU中所有可用的处理器,所以最主要的方法可以是使用fork-join框架(https://docs.oracle.com/ JavaSE的/教程/本质/并发/ forkjoin。HTML),它可以让你利用所有可用的处理器以及多线程... – hagrawal