为什么在我的素数测试中C#比Java和C ++慢得多

时间:2011-10-25 17:35:37

标签: c# java c++

(忍受我,在我的问题之前有很多解释)

正如你们中的一些人在过去几天可能知道的那样,我发布了一些与C ++性能相关的问题。作为一名Java程序员,我想知道C ++值得专注于额外的努力,或者Java是否足以进行性能编程。

无论如何,我决定编写一个基本(非高效算法)素数程序 - 没有对象创建,并看看3种语言如何比较时间。我不会发布代码,只是因为算法中的代码完全相同(除了我在C ++中使用int而不是boolean)。只有时序代码不同(这显然不在算法范围内)。

总之,计算前10000个素数并循环40次(夸大速度差异)需要:

Java:190.5秒

C ++:189秒

C#:242秒

计算前100000个素数(一次运行):

Java:591秒

C ++:588秒

C#:771秒

所以我的问题是,为什么C#比其他2慢得多?我认为Java和C#的时间非常接近。这是C#算法(我知道有更快的方法来查找素数):

static void Main(string[] args)
{

    int NumberOfPrimesToFind = 100000;
    int NumberOfRuns = 1;

    DateTime start = DateTime.Now;
    for (int k = 0; k < NumberOfRuns; k++)
    {
        FindPrimes(NumberOfPrimesToFind);

    }
    DateTime finish = DateTime.Now;
    Console.Out.WriteLine(finish-start);
    Console.In.Read();
}


static void FindPrimes(int NumberOfPrimesToFind)
{
    int NumberOfPrimes = 0;
    int CurrentPossible = 2;
    Boolean IsPrime ;

    while (NumberOfPrimes < NumberOfPrimesToFind)
    {
        IsPrime = true;

        for (int j = 2; j < CurrentPossible; j++)
        {
            if (CurrentPossible % j == 0)
            {
                IsPrime = false;
                break;
            }
        }

        if (IsPrime)
        {
            NumberOfPrimes++;
        }

        CurrentPossible++;
    }
}

C ++

int main()
{
    int NumberOfPrimesToFind = 100000;
    int NumberOfRuns = 1;
    int temp;
    double dif;

    time_t start,end;
    time (&start);

    for (int k = 0; k < NumberOfRuns; k++)
    {
        FindPrimes(NumberOfPrimesToFind);

    }

    time (&end);

    //Number of seconds
    dif = difftime (end,start);

    cout << (dif);
    cin >> temp;
}

void FindPrimes(int NumberOfPrimesToFind)
{
    int NumberOfPrimes = 0;
    int CurrentPossible = 2;
    int IsPrime ;

    while (NumberOfPrimes < NumberOfPrimesToFind)
    {
        IsPrime = 1;

        for (int j = 2; j < CurrentPossible; j++)
        {
            if (CurrentPossible % j == 0)
            {
                IsPrime = 0;
                break;
            }
        }

        if (IsPrime==1)
        {
            NumberOfPrimes++;
        }

        CurrentPossible++;
    }
}

爪哇:

public static void main(String[] args) {
    int NumberOfPrimesToFind = 100000;
    int NumberOfRuns = 1;

    long start = System.currentTimeMillis();
    for (int k = 0; k < NumberOfRuns; k++)
    {
        FindPrimes(NumberOfPrimesToFind);

    }
    long finish = System.currentTimeMillis();
    System.out.println((finish-start));

}

static void FindPrimes(int NumberOfPrimesToFind)
{
    int NumberOfPrimes = 0;
    int CurrentPossible = 2;
    Boolean IsPrime ;

    while (NumberOfPrimes < NumberOfPrimesToFind)
    {
        IsPrime = true;

        for (int j = 2; j < CurrentPossible; j++)
        {
            if (CurrentPossible % j == 0)
            {
                IsPrime = false;
                break;
            }
        }

        if (IsPrime)
        {
            NumberOfPrimes++;
        }

        CurrentPossible++;
    }
}

3 个答案:

答案 0 :(得分:12)

关于C#:

首先,你应该使用秒表代替日期时间。 对于代码计时,日期时间不可靠。

其次,你确定在Visual Studio关闭的发布模式下执行它吗? 如果visual studio已打开或您正在使用F5启动,JIT将不会优化代码!

所以...使用秒表并关闭所有visual studio实例。 您必须更改项目选项,您应该在顶部工具栏中有一个组合框,您可以在其中阅读“调试”,只需单击它并选择“发布”或右键单击您的项目,属性,然后将其更改为发布模式。然后为了避免各种问题,请关闭Visual Studio的所有实例,然后双击可执行文件启动。

请参阅http://msdn.microsoft.com/en-us/library/wx0123s5.aspx

CTRL + F5不能在发布模式下编译,它只是在选定的编译模式下启动可执行文件而不需要调试进程,所以如果它是在调试中编译的,它将启动在调试模式下编译的可执行文件而不调试它

然后我建议你避免使用布尔变量,每个分支条件可以减慢CPU,你可以使用整数来做。这适用于所有语言,而不仅仅是C#。

static void Main()
{
    const int NumberOfPrimesToFind = 100000;
    const int NumberOfRuns = 1;

    System.Diagnostic.Stopwatch sw = new System.Diagnostic.Stopwatch();

    sw.Start();
    for (int k = 0; k < NumberOfRuns; k++)
    {
        FindPrimes(NumberOfPrimesToFind);
    }
    sw.Stop();

    Console.WriteLine(sw.Elapsed.TotalMilliseconds);
    Console.ReadLine();
}

static void FindPrimes(int NumberOfPrimesToFind)
{
    int NumberOfPrimes = 0;
    int CurrentPossible = 2;

    while (NumberOfPrimes < NumberOfPrimesToFind)
    {
        int IsPrime = 1;

        for (int j = 2; j < CurrentPossible; j++)
        {
            if (CurrentPossible % j == 0)
            {
                IsPrime = 0;
                break;
            }
        }

        NumberOfPrimes += IsPrime;
        CurrentPossible++;
    }
}

当你在发布模式下使用C ++编译它时,因为输入参数是常量C ++编译器足够聪明,可以在编译时执行一些计算(现代C ++编译器的强大功能!)。这种魔法通常也与模板一起使用,例如STL(标准模板库)在调试模式下非常非常慢,但在发布模式下非常快。

在这种情况下,编译器完全排除了您的功能,因为您的函数输出未被使用。尝试使它返回一个整数,找到的素数,并打印出来。

int FindPrimes(int NumberOfPrimesToFind)
{
    int NumberOfPrimes = 0;
    int CurrentPossible = 2;
    while (NumberOfPrimes < NumberOfPrimesToFind)
    {
        int IsPrime = 1;

        for (int j = 2; j < CurrentPossible; j++)
        {
            if (CurrentPossible % j == 0)
            {
                IsPrime = 0;
                break;
            }
        }

        NumberOfPrimes += IsPrime;
        CurrentPossible++;
    }
    return NumberOfPrimes ;
}

如果您对C ++编译器的这个方面感到好奇,那么看看模板元编程,例如,存在一个C ++编译器完整的形式证明。正如维基百科所引用的“此外,模板是C ++中的图灵完成的编译时机制,这意味着计算机程序可表达的任何计算都可以通过运行之前的模板元程序以某种形式计算出来。” http://en.wikipedia.org/wiki/C%2B%2B

但是我真的希望你只是使用这个算法试图理解三个不同的编译器\系统的行为,因为,当然,这是你用来查找素数的最差算法,正如其他答案所指出的那样:)

答案 1 :(得分:1)

如果不相同,C ++和C#算法似乎非常相似。由于没有任何明显的代码差异可以解释性能,我怀疑可能存在局部性或优化器差异。确切知道的唯一方法是分析两个不同的版本并查看它们的不同表现。

答案 2 :(得分:-1)

在比较语言时,您应该首先尝试优化代码。否则,您可能会比较它执行效率低下的代码的效果,这可能不是很有意义。例如,在我看到Java显着优于C ++的所有基准测试中,它们都没有做任何有用的事情。 ;)

对于在40次运行中寻找100,000个素数的较长测试。

public static void main(String... args) {
    int numberOfPrimesToFind = 100000;
    int numberOfRuns = 40;

    long start = System.currentTimeMillis();
    for (int k = 0; k < numberOfRuns; k++)
        findPrimes(numberOfPrimesToFind);

    long finish = System.currentTimeMillis() - start;
    System.out.printf("Took %.2f seconds to loop %,d times%n", finish/1e3, numberOfRuns);
}

static void findPrimes(int numberOfPrimesToFind) {
    int numberOfPrimes = 0;
    int currentPossible = 2;

    while (numberOfPrimes < numberOfPrimesToFind) {
        boolean isPrime = isPrime(currentPossible);

        if (isPrime) {
            numberOfPrimes++;
        }

        currentPossible++;
    }
}

private static boolean isPrime(int currentPossible) {
    if ((currentPossible & 1) == 0) return false;
    for (int j = 3; j * j <= currentPossible; j += 2)
        if (currentPossible % j == 0)
            return false;
    return true;
}

打印

Took 9.26 seconds to loop 40 times

故事的寓意是你使用的算法通常比你使用的语言更重要。