【问题标题】:How to calculate overall computation time for a multi-threaded process如何计算多线程进程的总计算时间
【发布时间】:2018-04-08 17:09:02
【问题描述】:

我有一组任务,我们称之为T[],其中每个任务T[i] 需要一定的时间t(T[i]) 来处理。这些任务由X线程并行处理(这并不是说多个线程在一个任务上协同工作,而是多个任务由多个线程处理,每个线程执行一个任务,然后执行下一个任务, ETC)。

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

但我对X < size(T[]) 的情况感到很困惑(即我的线程比任务少)。如何以一种优雅的方式计算?

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

【问题讨论】:

  • 答案取决于调度算法。例如,如果您有 4 个持续时间为 10 秒的作业和一个持续时间为 100 秒的作业和 4 个线程,则如果调度程序将前 4 个每个 10 秒的作业作为第一组,然后在其上运行 100 秒的作业,则持续时间将为 110 秒。在第二轮拥有。如果调度器先按最长作业排序,则持续时间为 100 秒。
  • 对,我忘了添加这个信息。我已经更新了帖子。所以我知道任务会按照运行时间最长到运行时间最短的顺序进行处理,但我仍然不知道如何计算。
  • 您说的是“预期”时间,但从描述来看,调度似乎是确定性的。如果它确实是确定性的,并且下一个任务分配给最早的空闲线程,那么很容易模拟。如果它不是确定性的,则需要编辑问题以解释不确定性的来源。
  • 是的,请参阅我自己编辑的答案。我实际上最终模拟了它。它不是超级准确,但我的测试表明它足够准确。例如,我进行了一次跑步,估计需要 8 分钟,结果花了 9 分钟。我认为我对模拟的回答是正确的,但我不能接受自己的回答

标签: multithreading algorithm math parallel-processing


【解决方案1】:

我假设任务是按照提供的顺序安排的,并且每个任务都会转到第一个空闲的线程。如果这些假设是正确的,则没有有意义的不确定性——一个任务可能会转到任何空闲的线程(如果有多个线程),但这对总运行时间没有影响。

在这种情况下,我们可以使用大小为 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;
    }
}

【讨论】:

  • OP 提到任务是按“最长的优先” 基础处理的,而不是按呈现的顺序处理。
  • @MarkSetchell 该问题还指出,任务队列按最长优先排序,因此代码按原样运行。
  • 谢谢。我不知道像你在 Java 中的 python 代码这样的函数,所以对我来说下一个最好的事情就是实际模拟它,看看我自己的答案
  • 嗯,如果您只对 Java 解决方案真正感兴趣,最好用 java 标签标记您的问题。我并没有真正用 Java 编程,但 Python 的 minheap 的 Java 等价物是 java.util.PriorityQueue。我添加了代码的翻译。
  • 现在我已经玩过它了,这做得非常漂亮 - 可惜我不能投票两次!
【解决方案2】:

当执行顺序(大致)确定时,模拟测试运行是解决方案。我只是拿了我真正的处理代码,并用一个简单的 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

【讨论】:

  • 对于三个线程上的任务 [3, 2, 2, 2],我认为这会将最终任务安排在第一个线程上,总运行时间为 5。但它应该有运行时间 4:最终任务可以安排在其他两个线程之一上。一般来说,我很难理解为什么这段代码模拟了一个真实的系统——实际上,一个任务将被保留,直到一个线程空闲,然后分配给它。在您的代码中,这对应于将任务分配给总时间最短的线程,而不是当前和先前线程中最小的线程。
  • 我猜你还没有看到我一开始编辑的内容。是的,后来我意识到它有缺陷,解决方案是运行测试模拟。这给了我非常精确的估计,我刚刚在 5 个线程上运行了近 100 个运行时间非常不同的任务。它估计为 1 小时 39 分钟,而真正的跑步只差了几分钟
  • 不,我读过但误解了——我以为你是说第二种解决方案是缺陷较少的解决方案。也许您可以删除您认为错误的答案部分。
猜你喜欢
  • 2014-07-13
  • 2011-11-18
  • 1970-01-01
  • 2015-07-02
  • 2014-03-21
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2020-12-23
相关资源
最近更新 更多