简体   繁体   English

生成质数序列,Loop Break vs LINQ TakeWhile

[英]Generate prime number sequences, Loop Break vs LINQ TakeWhile

I try to generate prime number sequences with C#. The code seems to be working fine so far.我尝试用 C# 生成素数序列。到目前为止,代码似乎运行良好。

List<int> _primes = new List<int>();

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    foreach (var p in _primes)
    {
        if (p > chkEnd)
            break;
        if (num % p == 0)
            return false;
    }
    return true;
}

for (int i=2; i<1000000; i++)
{
    if (IsPrime(i))
        _primes.Add(i);
}

Then I change IsPrime() to use LINQ TakeWhile(), I think the two methods would have the same time complexity, but the execution time is significantly higher than the former.然后我把IsPrime()改成LINQ TakeWhile(),我觉得这两个方法的时间复杂度是一样的,但是执行时间明显比前者长。

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
    {
        if (num % p == 0)
            return false;
    }
    return true;
}

or要么

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
}

Does anyone has any idea?有谁知道吗?


Edit: I increased the loop iteration to 5000000, run the code in a consoleApplication and output the execution time编辑:我将循环迭代增加到 5000000,在 consoleApplication 中运行代码,执行时间为 output

00:00:01.1841593 -- IsPrime_PureLoopBreak 00:00:01.1841593 -- IsPrime_PureLoopBreak

00:00:03.2560654 -- IsPrime_TakeWhileAndLoop 00:00:03.2560654 -- IsPrime_TakeWhileAndLoop

00:00:03.4178782 -- IsPrime_TakeWhileAndAll 00:00:03.4178782 -- IsPrime_TakeWhileAndAll

    static void Main(string[] args)
    {
        List<int> _primes;

        bool IsPrime_PureLoopBreak(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            foreach (var p in _primes)
            {
                if (p > chkEnd)
                    break;
                if (num % p == 0)
                    return false;
            }
            return true;
        }

        bool IsPrime_TakeWhileAndLoop(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
            {
                if (num % p == 0)
                    return false;
            }
            return true;
        }

        bool IsPrime_TakeWhileAndAll(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
        }

        var t1 = Measure(() => 
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_PureLoopBreak(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t1} -- IsPrime_PureLoopBreak");

        var t2 = Measure(() =>
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_TakeWhileAndLoop(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t2} -- IsPrime_TakeWhileAndLoop");

        var t3 = Measure(() =>
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_TakeWhileAndAll(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t3} -- IsPrime_TakeWhileAndAll");

        Console.ReadLine();
    }

    public static TimeSpan Measure(Action action)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        action?.Invoke();
        stopwatch.Stop();
        return stopwatch.Elapsed;
    }

Consider the code the compiler would need to generate.考虑编译器需要生成的代码。 "High level" code like Linq is simple and easy to write, but is typically more difficult for the compiler to optimize well.像 Linq 这样的“高级”代码简单易写,但编译器通常更难优化。

Your former example could be further simplified to use a plain for loop rather than a foreach.您之前的示例可以进一步简化为使用普通的 for 循环而不是 foreach。 So each iteration should only need a handful of instructions.所以每次迭代应该只需要少数指令。

when using linq the compiler will generate a proxy object for the iterator.使用 linq 时,编译器将为迭代器生成代理 object。 This will need to call your lambda in order to check if it need to continue iteration, and the compiler might not be able to inline the method calls.这将需要调用您的 lambda 以检查它是否需要继续迭代,并且编译器可能无法内联方法调用。 Method calls are cheap, but still several times more expensive than simple arithmetic instructions.方法调用很便宜,但仍然比简单的算术指令贵几倍。

So as a rule of thumb, use Linq and other high level patterns to improve readability.因此,根据经验,使用 Linq 和其他高级模式来提高可读性。 If you discover that some part does not of the code do not fulfill the performance goals, use a profiler to identify exactly what part takes time, and rewrite that part until it is fast enough or you have made it as fast as you can.如果您发现代码的某些部分没有达到性能目标,请使用分析器来准确确定哪个部分需要时间,然后重写该部分直到它足够快或者您已使其尽可能快。

Using LINQ has some additional overhead.使用 LINQ 有一些额外的开销。 You are creating and calling lambda expressions.您正在创建和调用 lambda 个表达式。

But, make sure to try in a Release build before drawing conclusions.但是,请确保在得出结论之前尝试发布版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM