如何计算多线程进程的总体计算时间

时间:2018-04-08 17:09:03

标签: multithreading algorithm math parallel-processing

我有一组任务,我们称之为T[],其中每个任务T[i]需要处理一定的时间t(T[i])。任务由X线程并行处理(这并不意味着多个线程在单个任务上共同工作,但多个线程正在处理多个任务,每个线程执行一个任务,然后下一个等)。

现在我想计算处理所有任务所需的预计总时间。只要size(T[]) <= X当然很容易(即任务数量小于或等于线程数),在这种情况下,总时间等于最慢任务的时间。

但我对案例X < size(T[])感到很遗憾(即我的线程少于任务)。如何以优雅的方式计算出来?

编辑:根据评论员的要求,我们可以假设任务队列按最长运行任务排序,最后运行最短任务。此外,我们可以假设任务之间没有暂停,我们也可以忽略操作系统调度程序正在做什么。

2 个答案:

答案 0 :(得分:2)

我假设任务按照他们提供的顺序安排,并且每个任务都转到第一个免费的线程。如果这些假设是正确的,那么没有任何有意义的非确定性 - 任务可以进入任何自由的线程(如果有多个),但这对总运行没有影响时间。

在这种情况下,我们可以使用大小为X的最小堆(其中X是线程数)来模拟这一点,其中堆中的值表示其中一个线程的空闲时间。对于每个任务,我们从堆中弹出最早的线程,然后在它完成这个新任务的时候将其推回。

在我们安排完所有任务后,我们可以获取堆中的最大值,这将是所有任务完成的时间。

这是Python中相对较少的代码:

import heapq

def compute_time(tasks, X):
    threads = [0] * X
    for t in tasks:
        heapq.heappush(threads, heapq.heappop(threads) + t)
    return max(threads)

print compute_time([3, 2, 1], 2)
print compute_time([5, 4, 3, 3, 2, 1, 1], 3)

或者在Java中:

import java.util.*;

class Threads {
    public static void main(String args[]) {
        int totalTime1 = computeTotalTime(Arrays.asList(3, 2, 1), 2);
        System.out.println("totalTime1: " + totalTime1);

        int totalTime2 = computeTotalTime(Arrays.asList(5, 4, 3, 3, 2, 1, 1), 3);
        System.out.println("totalTime2: " + totalTime2);
    }

    static int computeTotalTime(List<Integer> task, int threads) {
        PriorityQueue<Integer> q = new PriorityQueue<Integer>();
        for (int i = 0; i < threads; i++) q.add(0);
        for (int t : task) q.add(q.poll() + t);
        int max = 0;
        while(!q.isEmpty()) max = q.poll();
        return max;
    }
}

答案 1 :(得分:0)

当执行顺序(大致)确定性时,模拟测试运行是解决方案。我刚刚接受了我的实际处理代码,并用一个简单的Thread.sleep替换它,同时睡眠任务预期要处理的时间(只是解释为毫秒来缩小它)。最后,我只是缩短了它再次花费的时间,结果非常好。我在5个线程上运行了近100个任务,执行时间大不相同。估计1小时39分钟,实际运行只有3分钟。

            long startSim = currentTimeMillis();
            List<Integer> taskTimes = parallelTests.getRuntimesForAllTests(); // ordered from longest time
            ThreadPoolExecutor simulationExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadCount);
            taskTimes.forEach(taskTime -> simulationExecutor.submit(() -> {
                try {
                    Thread.sleep(taskTime); // this is really seconds, but we just take it as milliseconds
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }));
            simulationExecutor.shutdown();
            simulationExecutor.awaitTermination(1, MINUTES);
            long stopSim = currentTimeMillis();
            long timeNeeded = stopSim - startSim;
            // now just multiply it *1000 to scale it up to seconds again, and that's your result