3

为了测试一些安全软件,我需要能够非常快速地在 Windows 中创建大量(可配置的)新进程(不是线程!),让它们存在一段时间(可配置),然后干净地终止. 这些进程根本不应该做任何事情——只存在指定的持续时间。

最终,我希望能够运行类似的东西:

C:\> python process_generate.py --processes=150 --duration=2500

这将很快创建 150 个新进程,让它们全部存活 2500 毫秒,然后让它们尽快终止。

作为起点,我跑了

from multiprocessing import Process
import os

def f():
    pass

if __name__ == '__main__':
    import datetime
    count = 0
    startime = datetime.datetime.now()
    while True:
        p = Process(target=f)
        p.start()
        p.terminate()
        count += 1
        if count % 1000 == 0:
            now = datetime.datetime.now()
            print "Started & stopped d processes in %s seconds" % (count, str(now-starttime))

并发现我可以在我的笔记本电脑上以大约 70 个进程/秒的速度连续创建和终止,而创建的进程会立即终止。大约 70 个进程/秒的速率持续了大约一个小时。

当我将代码更改为

from multiprocessing import Process
import os
import time

def f_sleep():
    time.sleep(1)

if __name__ == '__main__':
    import datetime
    starttime = datetime.datetime.now()

    processes = []
    PROCESS_COUNT = 100
    for i in xrange(PROCESS_COUNT):
        p = Process(target=f_sleep)
        processes.append(p)
        p.start()
    for i in xrange(PROCESS_COUNT):
        processes[i].terminate()
    now = datetime.datetime.now()
    print "Started/stopped %d processes in %s seconds" % (len(processes), str(now-starttime))

并为 PROCESS_COUNT 尝试了不同的值,我希望它的扩展性比它做的好得多。对于 PROCESS_COUNT 的不同值,我得到以下结果:

  • 0.72秒完成20个流程
  • 1.45秒完成30道工序
  • 3.68 秒完成 50 个流程
  • 14 秒内完成 100 个流程
  • 43 秒内完成 200 个流程
  • 300 道工序在 77 秒内完成
  • 111 秒内完成 400 个流程

不是我所期望的——我希望能够以合理的线性方式扩大并行进程数量,直到我遇到瓶颈,但我似乎几乎马上就遇到了进程创建瓶颈。根据我运行的第一个代码,我绝对希望能够在遇到进程创建瓶颈之前创建接近 70 个进程/秒的东西。

无需详细了解完整规格,笔记本电脑运行完全修补的 Windows XP,具有 4Gb RAM,否则处于闲置状态并且相当新;我认为它不会这么快就遇到瓶颈。

我的代码是否在这里做任何明显错误的事情,或者在 12 个月大的笔记本电脑上创建 XP/Python 并行进程真的那么低效?

4

5 回答 5

7

好吧,Windows 进程管理并不能很好地扩展。进程越多,将新进程插入调度所需的时间就越长。

现在将其与其他操作系统内核进行比较,例如 Linux,其中进程创建实际上是 O(1)(恒定时间),因为内核 2.6.8(当引入了具有此功能的调度程序时)。

请注意,我并不是想在这里向您推销 Linux。我只是建议您在不同的操作系统上试用您的程序,以亲自查看。

于 2012-06-22T07:23:13.177 回答
3

在分析和测试了一堆不同的场景之后,我发现在 Windows 下生成和杀死单个进程要快得多,而不是一次生成 N 个进程,杀死所有 N,然后重新启动 N。

我的结论是,Windows 保留了足够的可用资源来一次快速启动 1 个进程,但不足以在没有相当大的延迟的情况下启动 >1 个新的并发进程。正如其他人所说,Windows 启动新进程的速度很慢,但显然速度会随着系统上已经运行的并发进程数量而呈半几何下降 - 启动单个进程非常快,但是当您启动多个进程时你遇到了问题。这适用于存在的 CPU 数量、机器的繁忙程度(在我的测试中通常 <5% CPU)、Windows 是在物理服务器还是虚拟服务器上运行、有多少可用 RAM(我测试了高达 32Gb RAM,大约 24Gb 可用),... - 这似乎只是 Windows 操作系统的限制。

于 2012-07-12T10:03:40.443 回答
2

我已经在具有 4Gb RAM 的 Ubuntu 11.04 Dell Precision 中测试了您的代码,结果如下:

Started/stopped 100 processes in 0:00:00.051061 seconds
Started/stopped 150 processes in 0:00:00.094802 seconds
Started/stopped 200 processes in 0:00:00.153671 seconds
Started/stopped 300 processes in 0:00:00.351072 seconds
Started/stopped 400 processes in 0:00:00.651631 seconds
Started/stopped 470 processes in 0:00:01.009148 seconds
Started/stopped 1000 processes in 0:00:02.532036 seconds
Started/stopped 10000 processes in 0:00:29.677061 seconds

对于相同数量的进程,每次执行至少有 10% 的可变性,希望这很有用,在一秒钟内,我的计算机使用您的代码执行了近 500 个进程。

于 2012-06-22T08:24:55.263 回答
2

如果我没记错的话,与 Linux 相比,Windows 从来没有被设计为快速启动许多进程。这不是设计者认为你会做的事情——而在 linux 上,使用诸如此类的东西inetd,它是一个足够常见的操作模型来保证优化——因此,流程创建被优化得像地狱一样。

于 2012-06-22T08:29:37.853 回答
0

我想说的是,在 Linux 中创建许多 Python 进程也存在困难。运行 p.start() 500 次后,它变得非常慢。

有时我需要创建数千个可以长时间工作的流程。

在上面的示例中,一瞬间没有 PROCESS_COUNT 数量的活动进程,因为它们在 1 秒后开始完成工作。因此,如果在上述 2 秒内创建 1000 个进程,则超过一半的进程完成到创建过程结束。

from multiprocessing import Process
def sample():
        sleep(13)
start = time()
for i in range(1500):
    p = Process(target=sample)
    p.daemon = True
    p.start()
 end = time()
 print end - start 

我使用 SUSE ENTERPRISE 尝试了 140 核服务器,并在我的笔记本电脑上使用 Ubuntu - 动态是相同的(服务器结果):

500 processes start  - 1.36 s
1000 processes start - 9.7 s
1500 processes start - 18.4 s
2000 processes start - 24.3 s
3000 processes start - 43.3 s

这是因为在 fork 之前的这个调用。每个新的子进程都需要更长的时间

def _cleanup():
    # check for processes which have finished
    for p in list(_current_process._children):
        if p._popen.poll() is not None:
            _current_process._children.discard(p)

我记得,如果进程有 manager.Value 并且稍微重一点 - 它需要 10 GB 的 RAM 并且启动时间更长。

于 2015-09-03T16:47:29.943 回答