在 Linux 上创建线程与进程的开销

2024-05-26

我试图回答在 python 中创建线程与进程有多少开销的问题。我修改了类似问题的代码,该问题基本上运行一个带有两个线程的函数,然后运行带有两个进程的相同函数并报告时间。

import time, sys
NUM_RANGE = 100000000

from multiprocessing  import Process
import threading

def timefunc(f):
    t = time.time()
    f()
    return time.time() - t

def multiprocess():
    class MultiProcess(Process):
        def __init__(self):
            Process.__init__(self)

        def run(self):
            # Alter string + test processing speed
            for i in xrange(NUM_RANGE):
                a = 20 * 20


    for _ in xrange(300):
      MultiProcess().start()

def multithreading():
    class MultiThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)

        def run(self):
            # Alter string + test processing speed
            for i in xrange(NUM_RANGE):
                a = 20 * 20

    for _ in xrange(300):
      MultiThread().start()

print "process run time" + str(timefunc(multiprocess))
print "thread run time" + str(timefunc(multithreading))

然后我得到了多处理 7.9 秒和多线程 7.9 秒

我试图回答的主要问题是,在 Linux 上使用多线程或多处理是否适合处理数千个网络请求。看起来根据这段代码,它们在启动时间方面是相同的,但也许进程的内存使用量要大得多?


您的代码不适合对进程和线程之间的启动时间进行基准测试。多线程 Python 代码(在 CPython 中)意味着单核。当该线程持有全局解释器锁时,一个线程中的任何 Python 代码执行都将排除该进程中所有其他线程的执行(GIL https://wiki.python.org/moin/GlobalInterpreterLock)。这意味着只要涉及 Python 字节码,您就只能实现线程并发,而不是真正的并行。

您的示例主要是对特定的 CPU 限制工作负载性能(在紧密循环内运行计算)进行基准测试,无论如何您都不会使用线程。如果您想衡量创建开销,则必须从基准测试中删除除创建本身之外的任何内容(尽可能)。


TL; DR

启动一个线程(以 Ubuntu 18.04 为基准)比启动一个进程便宜很多倍。

与线程启动相比,使用指定 start_methods 的进程启动需要:

  • fork:约长 33 倍
  • 分叉服务器:约 6693 倍长
  • spawn:约 7558 倍长

完整结果在底部。


基准

我最近升级到 Ubuntu 18.04 并测试了一个脚本的启动,希望它更接近事实。请注意,此代码是 Python 3。

一些用于格式化和比较测试结果的实用程序:

# thread_vs_proc_start_up.py
import sys
import time
import pandas as pd
from threading import Thread
import multiprocessing as mp
from multiprocessing import Process, Pipe


def format_secs(sec, decimals=2) -> str:
    """Format subseconds.

    Example:
    >>>format_secs(0.000_000_001)
    # Out: '1.0 ns'
    """
    if sec < 1e-6:
        return f"{sec * 1e9:.{decimals}f} ns"
    elif sec < 1e-3:
        return f"{sec * 1e6:.{decimals}f} µs"
    elif sec < 1:
        return f"{sec * 1e3:.{decimals}f} ms"
    elif sec >= 1:
        return f"{sec:.{decimals}f} s"

def compare(value, base):
    """Return x-times relation of value and base."""
    return f"{(value / base):.2f}x"


def display_results(executor, result_series):
    """Display results for Executor."""
    exe_str = str(executor).split(".")[-1].strip('\'>')
    print(f"\nresults for {exe_str}:\n")

    print(result_series.describe().to_string(), "\n")
    print(f"Minimum with {format_secs(result_series.min())}")
    print("-" * 60)

基准测试函数如下。对于每一个测试n_runs,创建一个新管道。 一个新的进程或线程(执行器)启动并且目标函数calc_start_up_time立即返回时间差。就这样。

def calc_start_up_time(pipe_in, start):
    pipe_in.send(time.perf_counter() - start)
    pipe_in.close()


def run(executor, n_runs):

    results = []
    for _ in range(int(n_runs)):
        pipe_out, pipe_in = Pipe(duplex=False)
        exe = executor(target=calc_start_up_time, args=(pipe_in,
                                                    time.perf_counter(),))
        exe.start()
        # Note: Measuring only the time for exe.start() returning like:
        # start = time.perf_counter()
        # exe.start()
        # end = time.perf_counter()
        # would not include the full time a new process needs to become
        # production ready.
        results.append(pipe_out.recv())
        pipe_out.close()
        exe.join()

    result_series = pd.Series(results)
    display_results(executor, result_series)
    return result_series.min()

它的构建是通过 start_method 和作为命令行参数传递的运行次数从终端启动的。基准测试将始终运行n_runs使用指定的 start_method 启动进程(在 Ubuntu 18.04 上可用:fork、spawn、forkserver),然后与n_runs线程启动。结果侧重于最小值,因为它们显示了可能的速度。

if __name__ == '__main__':

    # Usage:
    # ------
    # Start from terminal with start_method and number of runs as arguments:
    #   $python thread_vs_proc_start_up.py fork 100
    #
    # Get all available start methods on your system with:
    # >>>import multiprocessing as mp
    # >>>mp.get_all_start_methods()

    start_method, n_runs = sys.argv[1:]
    mp.set_start_method(start_method)

    mins = []
    for executor in [Process, Thread]:
        mins.append(run(executor, n_runs))
    print(f"Minimum start-up time for processes takes "
          f"{compare(*mins)} "
          f"longer than for threads.")


Results

with n_runs=1000在我生锈的机器上:

# Ubuntu 18.04 start_method: fork
# ================================
results for Process:

count    1000.000000
mean        0.002081
std         0.000288
min         0.001466
25%         0.001866
50%         0.001973
75%         0.002268
max         0.003365 

Minimum with 1.47 ms
------------------------------------------------------------

results for Thread:

count    1000.000000
mean        0.000054
std         0.000013
min         0.000044
25%         0.000047
50%         0.000051
75%         0.000058
max         0.000319 

Minimum with 43.89 µs
------------------------------------------------------------
Minimum start-up time for processes takes 33.41x longer than for threads.

# Ubuntu 18.04 start_method: spawn
# ================================

results for Process:

count    1000.000000
mean        0.333502
std         0.008068
min         0.321796
25%         0.328776
50%         0.331763
75%         0.336045
max         0.415568 

Minimum with 321.80 ms
------------------------------------------------------------

results for Thread:

count    1000.000000
mean        0.000056
std         0.000016
min         0.000043
25%         0.000046
50%         0.000048
75%         0.000065
max         0.000231 

Minimum with 42.58 µs
------------------------------------------------------------
Minimum start-up time for processes takes 7557.80x longer than for threads.

# Ubuntu 18.04 start_method: forkserver
# =====================================


results for Process:

count    1000.000000
mean        0.295011
std         0.007157
min         0.287871
25%         0.291440
50%         0.293263
75%         0.296185
max         0.361581 

Minimum with 287.87 ms
------------------------------------------------------------

results for Thread:

count    1000.000000
mean        0.000055
std         0.000014
min         0.000043
25%         0.000045
50%         0.000047
75%         0.000064
max         0.000251 

Minimum with 43.01 µs
------------------------------------------------------------
Minimum start-up time for processes takes 6693.44x longer than for threads.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Linux 上创建线程与进程的开销 的相关文章

随机推荐

  • REST API 响应中的校验和

    发送带有响应内容的校验和是个好主意吗 如果是这样 计算校验和的最常见方法是什么 Example HTTP 1 1 200 OK Date Thu 30 Jun 2011 21 32 20 GMT Server Apache Connecti
  • 苹果的属性列表(plist)在C++中的实现

    我的任务是在 C 应用程序中读取 Apple 的属性列表文件 主要关注 OS X 中指定的 xml 类型 plist 文件 它模仿 xml 类型实现 Apple 对其属性列表的实现描述如下 http developer apple com
  • 计算热图颜色

    我正在制作一个由 HTML 表格组成的热图 该表包含n细胞并有一个lowest值和一个highest值 最高值始终高于最低值 每个细胞都有一个cell价值 所有这些值都是整数 具有最低值的单元格应为浅蓝色 缩放到具有最高值的单元格为深红色
  • Rails (PostgreSQL) 中文本列的默认大小

    如果我在迁移中有这个 t text body 我可以容纳多少文字 body 如果相关的话我正在使用 PostgreSQL 直接来自PostgreSQL 文档 http www postgresql org docs 8 4 static d
  • 自定义错误处理程序抛出错误:无法读取未定义的属性“get”(注入器)

    我正在 Angular 4 中构建自定义错误处理程序 以使用错误拦截器处理不同类型的应用程序错误 创建一个基类 应用程序错误 ts 和其他类 例如处理 403 错误创建类拒绝访问 ts 扩展了这个基类 在基类中注入了一个服务toastrSe
  • 用逗号分割字符串到新行

    我有一个像这样的字符串 This is great day tomorrow is a better day the day after is a better day the day after the day after that is
  • 将误差线添加到多条线上以在 R 中的绘图上显示标准差

    我有一个包含许多不同线条的图 我想为每条线上的每个点添加误差线 df lt matrix runif 25 5 5 plot 1 5 seq 0 1 1 4 type n mapply lines as data frame df col
  • .onLoad 在渲染完成之前调用吗?

    我想在页面加载后调用一些 JS 这可能会涉及延迟 因此我希望首先加载页面 以便显示内容 但似乎调用了 onLoad 处理程序中的代码before渲染完成 是否有更好的事件可以使用 该事件在页面 完成 时触发 澄清一下 我想在页面呈现在屏幕上
  • 使用 stl sort 对表进行排序

    我有一个巨大的表 约 50Gb 格式为 i j k 来自稀疏矩阵 存储为 uint32 t idx1 idx2 float vals uint32 t tablesize 我想使用给定的比较函数 即 idx1 和 idx2 的函数 对其进行
  • 卷积神经网络 (CNN) 输入形状

    我是 CNN 的新手 我有一个关于 CNN 的问题 我对 CNN 特别是 Keras 的输入形状有点困惑 我的数据是不同时隙的二维数据 比方说10X10 因此 我有 3D 数据 我将把这些数据输入到我的模型中来预测即将到来的时间段 所以 我
  • 如何配置Lettuce Redis集群异步连接池

    我正在配置我的生菜重新分配池 当我按照官方文档配置时 连接池无法正常初始化 无法获取连接 官方文档指出 RedisClusterClient clusterClient RedisClusterClient create RedisURI
  • 为什么我无法将动态事件处理程序附加到该元素?

    My code http jsfiddle net arEWv 7 HTML div style width 500px height 500px div div div
  • 从泛型类继承时需要 T 的列表或枚举器的建议

    我知道答案并不简单 而且我已经使用了一些 我认为丑陋的 木棍 我只是在寻找一些优雅的答案 抽象类 public interface IOtherObjects public abstract class MyObjects
  • 在 Spring 中使用事务时创建提交后

    由于某些原因 我使用 Spring PlatformTransactionManager 手动执行事务提交和回滚 我需要做的是设置一个钩子 以便在提交事务后发生提交后操作 通过查看 void commit TransactionStatus
  • 如何从带有短语主题的 Cortana 命令中提取参数,并通过文本激活?

    高水平 我想在中使用我的自定义 Cortana 命令 记事本 TEXT模式 例如 通过按 WIN S 并输入 appname Notepad Examples moment 这将打开记事本并输入 例句 记事本命令已经可以在VOICE模式 当
  • 在自定义 Dask 图中包含关键字参数 (kwargs)

    我正在使用 Dask 为一项操作构建自定义图表 熟悉如何将参数传递给 Dask 图中的函数 并阅读了docs http dask pydata org en latest custom graphs html 然而似乎还是缺少了一些东西 D
  • 为什么是 ”& ;”无效的语法?

    我正在尝试在终端上运行 for 循环 我希望将每次迭代发送到后台进程 以便所有迭代同时运行 以下是命令一一运行 for i in sra do fastq dump split files i done only 我突出显示了分号 要同时运
  • 删除单元格时表格视图单元格背景变为白色 - iOS

    我有一个 iOS 应用程序UITableView 我注意到当用户选择时单元格背景颜色会闪烁白色Delete button In the editActionsForRowAtIndexPath方法 我创建了两个单元格按钮 Edit and
  • 当应用程序继续运行时,如何清理 .NET 中的 COM 引用?

    我正在开发一个 NET 程序 该程序启动 Excel 的新实例 执行一些工作 然后结束 但必须让 Excel 保持运行 稍后 当程序再次运行时 它将尝试挂钩到前一个实例 在这种情况下处理 COM 对象释放的最佳方法是什么 如果我第一次没有对
  • 在 Linux 上创建线程与进程的开销

    我试图回答在 python 中创建线程与进程有多少开销的问题 我修改了类似问题的代码 该问题基本上运行一个带有两个线程的函数 然后运行带有两个进程的相同函数并报告时间 import time sys NUM RANGE 100000000