【问题标题】:Degrade performance of C# for-loop for bigger range over C for-loop降低 C# for-loop 的性能以获得比 C for-loop 更大的范围
【发布时间】:2015-02-11 16:44:12
【问题描述】:

当我从 C# 调用 ummanaged dll 时,我已经对 C# 和 C 中 for-loop 的性能进行了一些测试...

结果令我惊讶的是,随着循环越过更大的范围,C# 的性能与 C 相比有所下降。对于更小的范围,C# 表现出优于 C....但是,作为上限for-loop 增加,C# 的性能与 C 相比下降....

这是我的测试代码......

    [DllImport("Testing.dll", CallingConvention =  CallingConvention.Cdecl)]
    public static extern int SumLoop(int lowLimit, int highLimit);

    public static void Main(string[] args)
    {
        const int LowerRange = 1;
        const int HigherRange = 1000000;

        // Test with C# For Loop
        var watch1 = new Stopwatch();
        watch1.Start();
        int sum = 0;
        for (int i = LowerRange; i <= HigherRange; i++)
        {
            sum += i;
        }
        watch1.Stop();

        long elapseTime1 = watch1.ElapsedMilliseconds;

        // Test with C-for loop
        var watch2 = new Stopwatch();
        watch2.Start();
        int sumFromC = SumLoop(LowerRange , HigherRange);
        long elapseTime2 = watch2.ElapsedMilliseconds;
   }

Testing.dll:

__declspec(dllexport) int SumLoop(int lowLimit, int highLimit)
{
    int idx;
    int totalSum = 0;
    for(idx = lowLimit;idx<= highLimit; idx= idx +1)
    {
        totalSum += idx;
    }
    return totalSum;
}

测试结果:

测试 1:

更高的范围:1000000

C# 循环:4 毫秒

C 循环:9 毫秒

测试 2:

更高的范围:10000000

C# 循环:53 毫秒

C 循环:36 毫秒

测试 3:

更高的范围:100000000

C# 循环:418 毫秒

C 循环:343 毫秒

在这里,我开始进行上面的测试,目的是 C for 循环的性能将优于 C# 循环,但这与我的理解完全相反,并同意 question 并同意......但是当我增加上限时for 循环的范围,C 的性能比 C# 好...

现在,我在想这是测试方法错误还是预期的性能结果?

【问题讨论】:

  • 您在测试调试版本还是发布版本?
  • 尝试发布版本。 C# 的调试版本可能会慢很多。

标签: c# c performance for-loop unmanaged


【解决方案1】:

这里发生的情况是您忽略了使用 P/Invoke 调用 C 函数的固定开销。

C 函数比 C# 版本更快,但是因为调用它的开销相对较大,C 函数对于小型数组会显得较慢,因为调用开销相对较大占总时间的比例。

但是,随着您增加集合的大小,开销在总时间中所占的比例会越来越小,直到 C 版本的额外速度出现并且您开始看到它运行得更快。

如果您查看 C# 函数的时间,您会发现它确实与 N 或多或少呈线性增加,这是您所期望的。将 N 增加 100 倍后,将 T = 4 与 T = 418 进行比较。这正是您所期望的。但由于上述原因,C 次似乎并没有线性增加。

顺便说一句,如果你至少需要两个时间,你可以使用联立方程来求解:

T = K + XN

其中 K 是固定开销,X 是每个元素的开销。

我根据您的时间计算得出,调用非托管代码的固定开销约为 5.6 毫秒,每个元素的开销为 3.373737 x 10^-6 毫秒。

这个开销似乎有点大,但我猜测量的数据有些不准确。

【讨论】:

  • 由于我对 P/Invoke 了解不多,它是否会增加太多开销,以至于看起来 C 比 C# 慢。我不应该使用它还是有任何 P 的替代品/调用?
  • 恐怕别无选择(除了不调用非托管代码)。但是,您的发布版本是怎样的?
  • 在发布版本中,我没有注意到太大的区别:对于 10000000。它需要 52ms(C#) 和 40ms(C)....对于 100000000、418ms (C#) 和 335ms(C) )....
  • 这听起来不太可能,我认为你做错了。你是怎么运行的?如果按 F5 运行它,它仍然会进行调试运行。我尝试了你的代码,在我的系统上使用调试构建,c# 循环需要 220 毫秒,而发布版本需要 64 毫秒。您的代码似乎没有打印时间这一事实使我怀疑您是在调试器下运行它以检查变量。您需要添加 Console.WriteLine() 来打印结果,并在调试器外部运行它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
  • 2019-06-03
  • 2015-11-23
  • 1970-01-01
相关资源
最近更新 更多