了解Linux工作站上的软件并行化

时间:2016-07-06 17:08:52

标签: linux multithreading parallel-processing

摘要

我正在尝试了解执行多个模拟时计算资源的限制。我的任务在并行化方面是微不足道的 - 我需要运行大量简单的独立模拟,即每个模拟程序不依赖于另一个模拟程序。每个模拟的运行时间大致相同。为此,我创建了一个实验,详细说明如下。

详情

我有两个shell脚本位于同一目录中。

第一个名为simple的脚本:

#!/bin/bash
# Simple Script

echo "Running sleep with arg= $1 "
sleep 5s
echo "Finished sleeping with arg= $1"

第二个名为runall的脚本:

#!/bin/bash

export PATH="$PATH:./"

# Fork off a new process for each program by running in background
# Run N processes at a time and wait until all of them have finished
# before executing the next batch. This is sub-optimal if the running 
# time of each process varies significantly.


# Note if the number of total processes is not divisible by the alloted pool something weird happens

echo "Executing runall script..."

for ARG in  $(seq 600); do
    simple $ARG &
    NPROC=$(($NPROC+1))
    if [ "$NPROC" -ge 300 ]; then
        wait
        echo "New batch"
        NPROC=0
    fi
done

以下是我的电脑上的一些规格(MAC OS X):

$ ulimit -u
709
$ sysctl hw.ncpu
hw.ncpu: 8
$ sysctl hw.physicalcpu
hw.physicalcpu: 4

据此我解释说,我有709个进程供我使用,有8个处理器核可用。

然而,当我执行$ ./runall时,我最终得到了:

...
Running sleep with arg= 253 
Running sleep with arg= 254 
Running sleep with arg= 255 
Running sleep with arg= 256 
Running sleep with arg= 257 
Running sleep with arg= 258 
./runall: fork: Resource temporarily unavailable
Running sleep with arg= 259 
./simple: fork: Resource temporarily unavailable
Running sleep with arg= 260 
$ Running sleep with arg= 261
Finished sleeping with arg= 5
Finished sleeping with arg= 7
Finished sleeping with arg= 4
Finished sleeping with arg= 8
Finished sleeping with arg= 3
...

SO:

问题1 这是否意味着在709个可用流程中,只有258个可以专用于我的runall程序,剩下的可能还在我的计算机上的其他进程中使用?

问题2 我用其他东西代替simple脚本,这样做的事情比睡眠更复杂(它读取文件并处理文件中的数据以创建图形),现在我开始注意到一些差异。在使用$ time ./runall的帮助下,我可以获得总运行时间,而在调用simple之前最多可以获得258个进程之前,我总是得到大约5秒的运行时间:

real    0m5.071s
user    0m0.184s
sys     0m0.263s

,即并行运行多个模拟提供与单个模拟相同的运行时间。然而,现在我正在调用一个更复杂的程序而不是simple我得到的总运行时间比单个模拟时间长(调用单个模拟需要1.5秒,而20个并行模拟需要大约8.5秒)。我该如何解释这种行为?

问题3 我不确定处理器内核的数量与并行性能有什么关系 - 因为我有8个内核供我使用,我想我可以并行运行8个程序,同时我需要运行一个。我不确定我的理由......

2 个答案:

答案 0 :(得分:1)

如果您有8个可用的CPU线程,并且您的程序占用单个CPU的100%,那么一次运行8个以上的程序是没有意义的。

如果您的程序是多线程的,那么您可能希望一次运行的进程少于8个。如果您的程序偶尔使用不到100%的单个CPU(可能是他们等待IO),那么您可能希望一次运行8个以上的进程。

即使您的用户的进程限制非常高,其他资源也可能会更快耗尽 - 例如,RAM。如果你启动了200个进程并且它们耗尽了RAM,那么操作系统将通过将一些其他进程的RAM交换到磁盘来满足RAM请求来响应;现在计算机不必要地停止运行,因为200个进程正在等待IO从磁盘恢复内存,只是因为其他进程想要运行而再次写出来。这称为thrashing

如果您的目标是执行某些批量计算,那么加载计算机的步骤不足以使所有CPU核心保持100%利用率。更多的是浪费。

编辑 - 对术语的澄清。

  • 一台计算机可以有多个CPU插槽。
  • 单个CPU可以有多个CPU核心。
  • 单个CPU内核可以支持同时执行多个指令流。 Hyperthreading就是一个例子。
  • 指令流是我们通常称为"线程",在操作系统,进程或CPU中。

所以我可以拥有一个带有2个插槽的计算机,每个插槽包含一个4核CPU,其中每个CPU都支持超线程,因此每个核心支持两个线程。

这样的计算机可以同时执行2 * 4 * 2 = 16个线程。

单个进程可以拥有任意数量的线程,直到某些资源耗尽 - 原始RAM,内部操作系统数据结构等。每个进程至少有一个线程。

值得注意的是,像超线程这样的技巧可能无法线性扩展性能。当你有非交换式CPU核心时,这些核心包含足够的部分,能够自己执行单个指令流;除了内存访问,它不会与其他内核共享任何内容,因此性能可以线性扩展。

但是,每个核心都有很多部分 - 在某些类型的计算过程中,其中一些部分处于非活动状态而其他部分处于活动状态。而在其他类型的计算中可能相反。做了很多浮点数学?那么,核心中的整数数学单元可能是空闲的。做了很多整数数学?那么,浮点数学单元可能是空闲的。

超线程通过在核心内利用这些暂时未使用的单元来寻求增加性能,即使只是一点点;当浮点单元忙时,安排可以使用整数单元的东西。

...

就操作系统而言,涉及调度的是所有进程中可运行的线程数。如果我有一个进程有3个可运行线程,第二个进程有一个可运行线程,第三个进程有10个可运行线程,那么操作系统将要运行总共3 + 1 + 10 = 14个线程。

如果有更多的可运行程序线程而不是CPU执行线程,那么操作系统将尽可能多地运行,其他人将坐在那里无所事事,等待。同时,那些程序和那些线程可能已经分配了一堆内存。

假设我的计算机具有128 GB的RAM和CPU资源,这样硬件可以同时执行总共16个线程。我有一个使用2 GB内存来执行简单模拟的程序,该程序只创建一个执行其执行的线程,每个程序需要100个CPU时间才能完成。如果我试图同时运行该程序的16个实例会发生什么?

每个程序将分配2 GB * 16 = 32 GB的ram来保持其状态,然后开始执行其计算。由于每个程序都创建一个线程,并且有16个CPU执行线程可用,因此每个程序都可以在CPU上运行而不会争用CPU时间。我们需要等待整批完成的总时间为100秒:16个进程/ 16个CPU执行线程* 100个。

现在如果我将其增加到同时运行的32个程序怎么办?好吧,我们将分配总共64GB的RAM,并且在任何一个时间点,其中只有16个将运行。这很好,没有什么不好的事情发生,因为我们没有耗尽RAM(可能是任何其他资源),程序将全部有效运行并最终完成。运行时间大约是200秒时的两倍。

好的,如果我们尝试同时运行128个程序会发生什么?我们的内存不足:128 * 2 = 256 GB的内存,是硬件的两倍多。操作系统将通过将内存交换为dis并根据需要将其读回来进行响应,但它必须经常这样做,并且它必须等待磁盘。

如果你有足够的RAM,这将运行800s(128/16 * 100)。既然你没有,它很可能会花费更长的时间。

答案 1 :(得分:1)

你的问题有点令人困惑。但这是尝试解释其中的一些:

  

问题1这是否意味着在709个可用流程中,只有258个可以专用于我的runall程序,剩下的剩余部分可能正被我计算机上的其他进程使用?

正如ulimit manpage所述,-u告诉您可以以用户身份启动多少进程。如您所知,Unix上的每个进程都有uid(这里有一些细节,如euidsetuid等),它们指的是系统上的用户拥有那个过程。 -u告诉您的是,您(自登录并执行ulimit命令后)的进程数可以在计算机上启动并同时运行。请注意,一旦pid p的进程退出,操作系统可以自由地为其他进程回收该数字p

  

问题2

问题2的答案(这似乎是你的主要困惑)只有在我们理解time命令实际报告的内容时才能给出。理解time命令的输出需要一些实验。例如,当我使用100个进程(即$(seq 100))运行您的实验(在类似的Mac上)时,我得到:

./runall.sh  0.01s user 0.02s system 39% cpu 0.087 total

这意味着只有39%的可用计算能力被使用,导致{em>挂钟时间0.087s粗略地说挂钟时间乘以CPU利用率得出运行时间(代码需要的用户时间+系统调用需要执行的系统时间)。您的simple脚本太简单了。通过进行sleep系统调用,它不会导致CPU做任何工作!

将此示例与更真实的示例进行比较,以找到subset of a given set with given sum。此(Java)程序在同一台计算机上生成以下时间:

java SubsetSum  38.25s user 1.09s system 510% cpu 7.702 total

这意味着总挂钟时间约为7.7秒,但所有可用内核都非常强调执行此程序。在4 CPU(8逻辑CPU)上,我获得了500%的CPU利用率! (并且您可以看到挂钟时间(7.7)乘以CPU利用率(5.1),即39.27大致等于总时间(38.25+1.09 = 39.34))

  

问题3

然而,并行化程序的方法是找到解决问题的可并行化活动。您有8个可用核心,操作系统将决定如何将它们分配给要求它的进程。但是如果进程进入BLOCKING状态(在I / O上阻塞)会怎么样?然后,操作系统将安排此过程并安排其他内容。当您考虑调度的工作方式时,简单地看待“8核=同时8个程序”这一点很难实现。