C# 常见操作的成本是多少?

7
代码大全2(第601和602页)中,有一张“常见操作的成本”表格。
基准操作整数赋值被赋予值1,然后列出了Java和C++常见操作的相对时间。例如:
                                  C++        Java
Integer assignment                1             1
Integer division                  5             1.5
Floating point square root       15             4 

这个问题是关于是否有人拥有C#的相关数据。我知道这些不会对解决具体问题有所帮助,只是出于好奇。

(而且对于所有.NET语言来说,这都是相同的) - Robert Venables
看起来像是过早优化的过早优化...这些操作将在汇编代码中执行,但基准测试将在实际运行时运行,我认为它将测试基准测试是否慢,而不是机器指令。 - Jozef Izso
3个回答

12

我实施了书中的一些测试。我的电脑提供了一些原始数据:

测试运行#1:

TestIntegerAssignment 00:00:00.6680000
TestCallRoutineWithNoParameters 00:00:00.9780000
TestCallRoutineWithOneParameter 00:00:00.6580000
TestCallRoutineWithTwoParameters 00:00:00.9650000
TestIntegerAddition 00:00:00.6410000
TestIntegerSubtraction 00:00:00.9630000
TestIntegerMultiplication 00:00:00.6490000
TestIntegerDivision 00:00:00.9720000
TestFloatingPointDivision 00:00:00.6500000
TestFloatingPointSquareRoot 00:00:00.9790000
TestFloatingPointSine 00:00:00.6410000
TestFloatingPointLogarithm 00:00:41.1410000
TestFloatingPointExp 00:00:34.6310000

测试运行#2:

TestIntegerAssignment 00:00:00.6750000
TestCallRoutineWithNoParameters 00:00:00.9720000
TestCallRoutineWithOneParameter 00:00:00.6490000
TestCallRoutineWithTwoParameters 00:00:00.9750000
TestIntegerAddition 00:00:00.6730000
TestIntegerSubtraction 00:00:01.0300000
TestIntegerMultiplication 00:00:00.7000000
TestIntegerDivision 00:00:01.1120000
TestFloatingPointDivision 00:00:00.6630000
TestFloatingPointSquareRoot 00:00:00.9860000
TestFloatingPointSine 00:00:00.6530000
TestFloatingPointLogarithm 00:00:39.1150000
TestFloatingPointExp 00:00:33.8730000

测试运行#3:

TestIntegerAssignment 00:00:00.6590000
TestCallRoutineWithNoParameters 00:00:00.9700000
TestCallRoutineWithOneParameter 00:00:00.6680000
TestCallRoutineWithTwoParameters 00:00:00.9900000
TestIntegerAddition 00:00:00.6720000
TestIntegerSubtraction 00:00:00.9770000
TestIntegerMultiplication 00:00:00.6580000
TestIntegerDivision 00:00:00.9930000
TestFloatingPointDivision 00:00:00.6740000
TestFloatingPointSquareRoot 00:00:01.0120000
TestFloatingPointSine 00:00:00.6700000
TestFloatingPointLogarithm 00:00:39.1020000
TestFloatingPointExp 00:00:35.3560000

(每个基准测试10亿次,使用Optimize编译,AMD Athlon X2 3.0ghz,使用Jon Skeet的微基准测试框架,可在http://www.yoda.arachsys.com/csharp/benchmark.html上获得)

来源:

class TestBenchmark  
{  
[Benchmark]  
public static void TestIntegerAssignment()
{
int i = 1;
int j = 2;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j;
    }
}

[Benchmark]
public static void TestCallRoutineWithNoParameters()
{
    for (int x = 0; x < 1000000000; x++)
    {
        TestStaticRoutine();
    }
}

[Benchmark]
public static void TestCallRoutineWithOneParameter()
{
    for (int x = 0; x < 1000000000; x++)
    {
        TestStaticRoutine2(5);
    }
}

[Benchmark]
public static void TestCallRoutineWithTwoParameters()
{
    for (int x = 0; x < 1000000000; x++)
    {
        TestStaticRoutine3(5,7);
    }
}

[Benchmark]
public static void TestIntegerAddition()
{
    int i = 1;
    int j = 2;
    int k = 3;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j + k;
    }
}

[Benchmark]
public static void TestIntegerSubtraction()
{
    int i = 1;
    int j = 6;
    int k = 3;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j - k;
    }
}

[Benchmark]
public static void TestIntegerMultiplication()
{
    int i = 1;
    int j = 2;
    int k = 3;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j * k;
    }
}


[Benchmark]
public static void TestIntegerDivision()
{
    int i = 1;
    int j = 6;
    int k = 3;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j/k;
    }
}

[Benchmark]
public static void TestFloatingPointDivision()
{
    float i = 1;
    float j = 6;
    float k = 3;

    for (int x = 0; x < 1000000000; x++)
    {
        i = j / k;
    }
}

[Benchmark]
public static void TestFloatingPointSquareRoot()
{
    double x = 1;
    float y = 6;

    for (int x2 = 0; x2 < 1000000000; x2++)
    {
        x = Math.Sqrt(6);
    }
}

[Benchmark]
public static void TestFloatingPointSine()
{
    double x = 1;
    float y = 6;

    for (int x2 = 0; x2 < 1000000000; x2++)
    {
        x = Math.Sin(y);
    }
}

[Benchmark]
public static void TestFloatingPointLogarithm()
{
    double x = 1;
    float y = 6;

    for (int x2 = 0; x2 < 1000000000; x2++)
    {
        x = Math.Log(y);
    }
}

[Benchmark]
public static void TestFloatingPointExp()
{
    double x = 1;
    float y = 6;

    for (int x2 = 0; x2 < 1000000000; x2++)
    {
        x = Math.Exp(6);
    }
}

private static void TestStaticRoutine() {

}

private static void TestStaticRoutine2(int i)
{

}

private static void TestStaticRoutine3(int i, int j)
{

}

private static class TestStaticClass
{

}

我会对结果进行一些操作(比如Console.WriteLine它),只是为了确保它不被优化掉。虽然它可能不会被优化,但... - Mark Simpson
你不觉得每个结果都是 0.33、0.66 或 ~0.99 有点奇怪吗?特别是当调用带有 0、1、2 个参数的函数时,得到的结果分别是 0.9、0.6、0.9,我会对测试方法产生怀疑。此外,私有静态函数可以内联。在我看来,那是一个非常糟糕的测试。 - viraptor
这个基准测试是虚假的。每个测试都有执行1000000000次加法的开销,因为循环中使用了加法。为了正确地进行此测试,您可能需要计算执行1000000000个数字的加法所花费的时间。 - bunggo

4

3
这是一个合理的问题,但我看到的几乎所有性能问题,尤其是在Java和C#中,都归结为以下两点:
  • 过多的抽象层
  • 依赖基于事件通知的编码风格
这与��本操作没有多少关系。 抽象的问题在于在工作量变大时就不太好了。每个层次通常都会带来一些小的性能损失,并且这些性能损失会以复合方式累积。此时你开始需要解决办法。(我认为StringBuilder就是这样一种解决办法。)
基于事件通知的编码风格(而不是通过定期处理保持一致的简单数据结构)的问题在于,看起来简单的操作(例如将属性设置为值)可能会导致数据结构中的一连串操作。做的事情比人们预期的要多得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接