【问题标题】:Safe thread stack size?安全线程堆栈大小?
【发布时间】:2011-05-08 11:02:57
【问题描述】:

我正在编写一些产生相当多线程的代码(目前大约有 512 个,但将来可能会更高)。每个线程只执行少量操作,因此我希望将线程在系统上的开销保持在最低限度。

我正在使用pthread_attr_setstacksize() 设置堆栈大小,我可以从PTHREAD_STACK_MIN 获得允许的最小堆栈大小。但我的问题是:使用PTHREAD_STACK_MIN 作为线程堆栈大小是否安全?我该如何计算我需要多少堆栈?是否有任何隐藏的开销需要添加到我的计算中?

另外,我可以使用其他任何技术来减少线程对系统的负担吗?

【问题讨论】:

    标签: c pthreads stack-size


    【解决方案1】:

    你不应该在这么多线程附近创建任何地方,而且你绝对不应该创建一个新线程来执行少量操作。当且仅当您现有的线程完全饱和并且有更多可用的物理或逻辑护理工作时,您才应该创建一个新线程。这将一个合理的当前应用程序硬限制在大约 10 个线程左右,即使您在六核上运行,您最多也只需要 12 个左右。这样的设计有很大的缺陷,会占用大量的进程内存,并不会真正提升性能。

    至于堆栈大小,您无法真正计算任意线程需要多少,因为它完全取决于代码运行。但是,在 Visual Studio 中,典型的堆栈大小为几兆字节。您必须发布整个代码以及线程执行的反汇编代码才能知道要使用多少堆栈大小。只需保持几兆字节即可。

    【讨论】:

    • +1:但是如果您的线程经常受 IO 限制,您可能需要比内核更多的线程...
    • @C. Ross:取决于它们的 I/O 绑定程度。好的,所以还有其他构建异步 I/O 系统的方法可以更有效地利用资源(在我的时间里可能效率较低,这取决于我对事件队列的使用程度,select 等),但是如果我的线程将花费 99.8% 的时间等待网络 I/O,那么我想要 500 个线程。有时 RAM 比程序员便宜,有时则不然 ;-)
    • @DreadMG:感谢您的回复。也许我应该更明确地说明我正在编写的代码。我 am 非常受 IO 限制。我试图接近我的 SSD 允许的最大 IOPS 数(~16,000)。我正在从一个大文件(~4GB)中读取许多(~66,000)小(
    • @史蒂夫真,真。如果这是您想要做的,请确保您正在运行 64 位内存堆负载。
    • @ltn100:你没提到! :P 如果您所做的只是一点点 I/O,那么您可能根本不需要太多堆栈空间。我仍然认为,如果您使用 512 个线程,那么您的 I/O 代码中一定是非常非常错误的。为什么不只是内存映射整个文件并从堆中读取?
    【解决方案2】:

    所需的堆栈帧大小取决于您使用的编译器,基本上您可以尝试猜测自动变量、参数的大小以及返回地址、保存寄存器等的一些开销。

    您应该考虑是否可以使用Thread Pool。因为创建线程不是免费的。

    【讨论】:

    • 我怀疑线程池是否对现代实现有显着的好处。没有人再使用LinuxThreads 和这样的黑客了。你有关于线程创建相对成本的最新参考吗?
    • 确实如此。线程创建的成本各不相同,但现在通常很便宜。但是我认为关于线程池的观点仍然存在。它们是一个值得考虑的有用工具,但我发现大多数人只是试图找到使用它们的理由。
    【解决方案3】:

    减少线程堆栈大小不会减少开销(不是在 CPU、内存使用或性能方面)。您在这方面的唯一限制是为您的平台上的线程提供的总可用虚拟地址空间。

    我会使用默认堆栈大小,直到平台出现其他问题(如果它发生的话)。然后在出现问题时尽量减少堆栈使用。然而,这些会导致真正的性能问题,因为您需要在堆中或 devise thread-dependent allocation 其他地方进行访问。

    隐藏的开销可能包括:

    • 在堆栈上分配大型数组,例如通过VLAalloca() 或只是简单的静态大小的自动数组。
    • 您无法控制或不知道使用模板、工厂类等后果的代码。但是,鉴于您没有指定 C++,这不太可能成为问题。
    • 从库头文件等中导入代码。这些可能会在版本之间发生变化,并显着改变它们的堆栈,甚至线程使用情况。
    • 递归。这也是由于上述几点,请考虑诸如boost::bind、可变参数模板、疯狂的宏,然后只是使用缓冲区或堆栈上的大对象的一般递归。

    除了设置堆栈大小之外,您还可以根据需要操纵线程优先级以及暂停和恢复它们,这将大大有助于调度程序和系统响应能力。 Pthreads 允许您设置争用范围; LWP 和范围调度的性能特征差异很大。

    这里有一些有用的链接:

    【讨论】:

    • 谢谢马特。我认为这最接近我之后的答案。我希望有人会说“将所有局部变量的 sizeof() 相加,为线程开销增加 42 个字节,然后为好运增加 10%”,但似乎还有更多的因素正在上映。感谢您提供有关优先级和暂停的提示。
    • @ltn100:不,它更像是“在检测溢出的调试上下文中,使用指定的堆栈大小彻底测试您的代码。然后添加 10% 以求好运”。
    • @ltn100:如果你能找到一个调试工具,它可以在你的平台上实际测量高水位标记堆栈使用情况,那很好,但是你实际需要这样做的平台(因为堆栈是物理 RAM前面)不一定是最好的调试工具。而且您必须真正彻底地进行测试。任何小事,包括对动态链接的库的更改,或者他们查看的环境变量,或者(字面意思)一周中的哪一天,都可能增加你的高水位线。避免使用 VLA,避免任何依赖于输入数据的递归。
    • @Steve Jessop:说得对。不错的术语,以及关于外部代码/递归的警告。
    猜你喜欢
    • 1970-01-01
    • 2010-10-21
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 2012-03-24
    相关资源
    最近更新 更多