【问题标题】:Java thread creation performance vs C# thread creation performance vs C++ (native threads)? [closed]Java 线程创建性能与 C# 线程创建性能与 C++(本机线程)? [关闭]
【发布时间】:2013-10-16 16:43:00
【问题描述】:

我很感兴趣在 Java、C# 和 C++ 中创建线程的实际成本是多少?我知道当线程在创建时必须完成一堆操作:分配线程堆栈,初始化描述符等。

但我对实际成本很感兴趣。 C# 和 Java 使用不同的 VM 和不同的 JIT,而 C++ 执行本机代码。因此,所有这些语言的线程创建时间都不同。我还听说在 Java 中创建线程比在 C# 中慢得多。有人可以就这个问题给出权威的答案和解释吗?

【问题讨论】:

  • 一般来说,权威的回答是“如果线程creation是你应用程序的瓶颈,那么你做错了”。您不应该如此频繁地创建和销毁线程,以免造成任何 的不同。如果您有许多频繁启动和停止的短期任务,请使用线程池。
  • @jalf:有时您需要在依次执行两个任务和将第二个任务作为新线程启动之间进行选择,这样它就不会阻塞当前线程或被第一个任务阻塞。您感兴趣的是哪个可以最快地完成工作哪个可以最好地利用 CPU。了解启动新线程的成本可以帮助您做出决定。
  • @RalphChapin 请重新阅读我的评论。我没有说“按顺序做所有事情”,我说您可以并行运行它们,而无需为每个任务启动一个新线程。这就是线程池的用途。您可以并行运行任务而不会产生产生新线程的成本。
  • “让一堆线程闲置”不会消耗内存。在最坏的情况下,它会消耗一些地址空间。但是,是的,任何事情都需要权衡取舍。我的观点很简单,如果你如此频繁地启动新线程以至于线程启动成本很重要,那么你不应该这样做,而是重用现有线程——例如通过线程池。
  • 这不是特定于实现的吗?

标签: c# java c++ multithreading performance


【解决方案1】:

用 C#、Java 和 Visual C++ 创建 10,000 个线程的基准测试:

C#

class Program
{
    static void Main(string[] args)
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 10000; i++)
        {
            Thread thread = new Thread(DoNothing);
            thread.Start();
        }
        watch.Stop();
        Console.WriteLine(watch.Elapsed);
    }

    static void DoNothing()
    {
        //Do Nothing
    }
}

结果:1​​.7638042 秒

Java

public class ThreadBencher {

    public static void main(String[] args) {
        Runnable r = new Runnable() {
             public void run() {
                 //Do nothing
             }
         };

         long startTime = System.nanoTime();
         for (int i = 0; i < 10000; i++) {
             Thread thread = new Thread(r);
             thread.start();
         }
         long stopTime = System.nanoTime();
         long totalTime = stopTime - startTime;
         System.out.print(totalTime);

    }

}

结果:1​​.514557805 秒(或 1514557805 纳秒)

Visual C++

DWORD WINAPI DoNothing( LPVOID lpParam ) 
{
    return 0;
}

void main()
{
    HANDLE ourThreadHandle = 0;
    SYSTEMTIME st1;
    SYSTEMTIME st2;
    int i;
    GetLocalTime(&st1);
    for (i = 0; i < 10000; i++) {
        ourThreadHandle = CreateThread( NULL, 0, DoNothing, 0, 0, NULL);
    }
    GetLocalTime(&st2);
    double dblSt1 = st1.wSecond + (st1.wMilliseconds / 1000);
    double dblSt2 = st2.wSecond + (st2.wMilliseconds / 1000);
    double result = dblSt2 - dblSt1;
    cout << st1.wSecond << "." << st1.wMilliseconds << endl;
    cout << st2.wSecond << "." << st2.wMilliseconds << endl;
}

结果(根据输出手动计算后):0.925 秒

(免责声明:我不太了解 C++,所以 C++ 代码非常拼凑在一起)

注意:这是在 64 位 Windows 8 环境中完成的。

【讨论】:

  • 很抱歉,但这对于任何语言都不是一个很好的基准,因为在 c# 和 Java 中,您总是必须考虑对象被释放的方式(以及何时!),并防止它影响结果。在现实生活中,结果可能与您提供的结果非常接近或非常远。作为基准,您的 c++ 示例比其他两个示例更可靠……因为您从未破坏过线程。
  • @daniel.gindi 关于如何解决这个问题有什么建议吗? (鉴于我们只是想比较不同语言在线程创建方面的表现,而不是它们在对象释放方面的表现。)
  • @daniel.gindi:释放是一回事,但是热身呢?没有太多代码需要预热,但对我来说,使用 Caliper 它的运行速度大约是原来的两倍(这可能是由于不同的硬件)。
  • 按照您的操作方式,它们可以在 for 循环期间被释放。您可以尝试以某种方式禁用垃圾收集器,或者将对线程及其函数的引用保存在数组中。 (然后你可以运行另一个循环来测量数组操作时间并减去它)
  • @maaartinus 是的,预热在这里并不是像垃圾收集器和释放那样的大问题,因为线程是本机资源,但准确地说,您仍然需要考虑这一点。
【解决方案2】:

这是创建线程时发生的情况。

创建新线程时,它会与其他线程共享其代码段、数据段和操作系统资源(如打开的文件)。但它被分配了自己的堆栈、寄存器集和程序计数器。

线程成本

线程在内存使用和性能方面对您的程序(和系统)来说是一个真正的成本。每个线程都需要在内核内存空间和程序的内存空间中分配内存。

管理线程和协调其调度所需的核心结构使用有线内存存储在内核中。线程的堆栈空间和每个线程的数据存储在程序的内存空间中。

这些结构中的大多数都是在您第一次创建和初始化时创建的 创建线程——一个相对昂贵的过程,因为 与内核的所需交互。

其中一些成本是可配置的,例如为辅助线程分配的堆栈空间量。创建线程的时间成本是一个粗略的近似值,仅用于相互比较。线程创建时间可能因处理器负载、计算机速度以及可用系统和程序内存量而有很大差异。

线程不消耗内存(除了它们的堆栈,即 恒定大小);进程消耗内存。线程的全部点 是他们共享进程状态。

公共语言运行时 (CLR) 线程的堆栈空间设置为 (由 CLR 提交)默认为 1MB(64 位代码线程为 4MB)。

在 C++ 中,它为堆栈保留 1MB(它映射其地址空间),但它不一定分配在物理内存中,只是其中的一小部分。如果堆栈增长超过该值,则会生成页面错误并分配更多物理内存。

Java 线程的创建成本很高,因为涉及到相当多的工作:

  • 必须为线程堆栈分配和初始化一大块内存。

  • 需要进行系统调用来创建/向主机操作系统注册本机线程。

  • 需要创建、初始化描述符并将其添加到 JVM 内部数据结构中。

从某种意义上说,只要线程还活着,它就会占用资源,这也是昂贵的;例如线程堆栈、堆栈中可访问的任何对象、JVM 线程描述符、OS 本机线程描述符。

【讨论】:

  • 谢谢。这是一个很好的解释。但是,您能否像为 Java 一样添加有关 CLR 线程创建的更多详细信息?正如我在 CLR 中所理解的,这一步:“需要创建、初始化并添加到 JVM 内部数据结构中的描述符”不需要?
  • 不客气,有关 CLR 的更多详细信息在此链接中,请看一下。 stackoverflow.com/questions/10121943/…
【解决方案3】:

C++ 通常是一种运行速度更快的语言,尽管它可能是一种更难学习的语言;因此,了解更多基本功能需要更长的时间,并且可能会在您制作应用程序时减慢编码过程

【讨论】:

    猜你喜欢
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多