美文网首页
C# Parallel.For的使用

C# Parallel.For的使用

作者: TauJiong | 来源:发表于2019-09-28 20:49 被阅读0次

    1 缘起

    实验室接了一个新的项目,需要在一块3d地形上基于速度绘上颜色,而且要动态改变。由于地形比较大,mesh网格的顶点一共有一千三百多万个,即使在逻辑上优化之后也达到了一百多万个。使用传统的for循环为每个顶点赋颜色值,效率太低,最终的fps只能达到8左右,完全达不到项目要求。在老师的指导下,开始考虑多线程的方法提高效率。

    2 Parallel.For

    在查阅了大量网络资源之后,确定了最简单易行的方法是C#本身提供的Parallel并行计算方法。

    关于Parallel.For的语法就不细说了,直接通过下面的代码见识一下并行的威力吧。

    using System.Diagnostics;
    using System.Threading.Tasks;
    using UnityEngine;
    
    public class testParallel : Mono Behaviour
    {
        Stopwatch stopWatch = new Stopwatch();
        void Start()
        {
            stopWatch.Start();
            for (int i = 0; i < 10000; i++)
            {
                for (int j = 0; j < 60000; j++)
                {
                    int sum = 0;
                    sum += i;
                }
            }
            stopWatch.Stop();
            print("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");
    
            stopWatch.Reset();
            stopWatch.Start();
            Parallel.For(0, 10000, item =>
            {
                for (int j = 0; j < 60000; j++)
                {
                    int sum = 0;
                    sum += item;
                }
            });
            stopWatch.Stop();
            print("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");
        }
    }
    

    在unity中运行该代码,其结果如下图所示

    可以发现,Parallel.For相较于传统的for循环运行时间加快了1s。而在本次项目中,在使用了Parallel.For的情况下,fps直接达到了23,这是一个相当大的提升了。

    3 深入思考

    在体验到Parallel.For的巨大优势之后,我不禁思考:

    1. 是不是所有的Parallel.For都比传统的for循环都快呢?
    2. 今后我是不是可以把所有的for循环都替换为Parallel.For呢?

    带着这样的问题,我继续搜索,也发现了使用Parallel.For的一些注意事项。

    3.1 Parallel.For的快是有前提的

    众所周知,在实现多线程时,为了防止多个线程同时处理同一个变量而导致变量处于"薛定谔状态",引入了"锁"的概念,即在每一时刻只有获得"锁"的线程才能操作目标变量。那么如果在Parallel.For中也需要操作一个全局变量,就意味着即使这是并行计算,大家也需要排队操作全局变量,此时Parallel.For可能远远不如传统的for循环来的快。

    using System.Collections.Concurrent;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using UnityEngine;
    
    public class testParallel : MonoBehaviour
    {
        Stopwatch stopWatch = new Stopwatch();
        void Start()
        {
            var obj = new Object();
            long num = 0;
            ConcurrentBag<long> bag = new ConcurrentBag<long>();
    
            stopWatch.Start();
            for (int i = 0; i < 10000; i++)
            {
                for (int j = 0; j < 60000; j++)
                {
                    num++;
                }
            }
            stopWatch.Stop();
            print("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");
    
            stopWatch.Reset();
            stopWatch.Start();
            Parallel.For(0, 10000, item =>
            {
                for (int j = 0; j < 60000; j++)
                {
                    lock (obj)
                    {
                        num++;
                    }
                }
            });
            stopWatch.Stop();
            print("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");
        }
    }
    

    在unity中运行该代码,其结果如下图所示

    可以发现,Parallel.For的运行时间竟然是传统for循环运行时间的100倍!因此可以得出结论:

    如果在循环中需要竞争资源,用到线程锁的话,Parallel.For未必优于传统for循环

    3.2 Parallel.For的循环是无序的

    由于Parallel.For中的各个循环是同时进行的,所以每次循环体执行的时间会有些许差异,这就导致了循环的运行是无序的。

    using System.Threading.Tasks;
    using UnityEngine;
    
    public class testParallel : MonoBehaviour
    {
        void Start()
        {
            Parallel.For(0, 5, item =>
            {
                print(item);
            });
        }
    }
    

    在unity中运行该代码,其结果如下图所示

    因此可以得出结论:

    如果循环的执行顺序需要严格控制的话,则不能使用Parallel.For

    4 总结

    在计算机多核处理器普及的前提下,合理的使用多线程或者并行能够显著提高软件的运行效率。当然,这一切都是有前提的。滥用多线程往往会适得其反,不仅得不到理想的输出结果,甚至会干扰其他程序的正常运行。在明确了自己的需求的前提下,选择合适的方法才是最重要的。

    相关文章

      网友评论

          本文标题:C# Parallel.For的使用

          本文链接:https://www.haomeiwen.com/subject/xwtzuctx.html