【问题标题】:Speeding up a C# loop加速 C# 循环
【发布时间】:2014-08-01 15:55:40
【问题描述】:

我正在尝试找到一种快速方法来加快以下for loops 的速度。 (只是有趣的理论)

我有以下程序

using System;
using System.Collections;
using System.Diagnostics;

namespace SpeedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();

            ArrayList widgets = new ArrayList(50000);

            for (int i = 0; i < widgets.Capacity; i++)
            {
                widgets.Add(new Widget(i));
            }

            string ProcessingMessages = "";

            for (int i = 0; i < widgets.Count; i++)
            {
                Widget widgetToProcess = (Widget)widgets[i];
                ProcessingMessages += widgetToProcess.ProcessWidget();
                ProcessingMessages += "\r\n";
            }

            Console.WriteLine(ProcessingMessages);

            timer.Stop();

            Console.WriteLine(timer.Elapsed);
            Console.ReadLine();
        }
    }

    class Widget
    {
        public Widget()
        {
            this.CreatedDateTime = DateTime.Now;
        }

        public Widget(int ID) : this()
        {
            this.ID = ID;
        }

        public DateTime CreatedDateTime { get; set; }
        public int ID { get; set; }

        public string ProcessWidget()
        {
            int Z = this.ID + this.ID;
            string Message = "Widget ID " + this.ID;
            Message += " was created on ";
            Message += CreatedDateTime.Year + "-" + CreatedDateTime.Month + "-" + CreatedDateTime.Day + ", at ";
            Message += CreatedDateTime.Hour + ":" + CreatedDateTime.Minute + ", with a value of ";
            Message += Z.ToString();
            return Message;
        }
    }
}

我怎样才能加快这些for loops 自身的速度?我知道实现并行性会很有用,例如

for (int i = 0; i < widgets.Count; i += 10)
            {
                Widget widgetToProcess1 = (Widget)widgets[i];
                Widget widgetToProcess2 = (Widget)widgets[i+1];
                Widget widgetToProcess3 = (Widget)widgets[i+2];
                Widget widgetToProcess4 = (Widget)widgets[i+3];
                Widget widgetToProcess5 = (Widget)widgets[i+4];
                Widget widgetToProcess6 = (Widget)widgets[i + 5];
                Widget widgetToProcess7 = (Widget)widgets[i + 6];
                Widget widgetToProcess8 = (Widget)widgets[i + 7];
                Widget widgetToProcess9 = (Widget)widgets[i + 8];
                Widget widgetToProcess10 = (Widget)widgets[i + 9];
                ProcessingMessages += widgetToProcess1.ProcessWidget() + "\r\n" +
                    widgetToProcess2.ProcessWidget() + "\r\n" +
                    widgetToProcess3.ProcessWidget() + "\r\n" +
                    widgetToProcess4.ProcessWidget() + "\r\n" +
                    widgetToProcess5.ProcessWidget() + "\r\n" +
                    widgetToProcess6.ProcessWidget() + "\r\n" +
                    widgetToProcess7.ProcessWidget() + "\r\n" +
                    widgetToProcess8.ProcessWidget() + "\r\n" +
                    widgetToProcess9.ProcessWidget() + "\r\n" +
                    widgetToProcess10.ProcessWidget() + "\r\n";
             }

然而,这都是手动编写的。我正在尝试找到一种方法来动态分解大型任务/数组大小并更快地运行这些块。

在这方面寻找一些简单的想法/概念。或者,如果有比使用for 循环进行此测试更好的选择。

【问题讨论】:

  • 几个 cmets:循环本身不会成为瓶颈。您在循环中所做的工作可能是。您正在使用动态字符串 concat 会产生大量开销。此外,您正在使用ArrayList,这不是当今最好的方法。 C# 具有泛型,因此请使用它们。
  • @Brian 是对的,对,对。请学习this absurdly simple technique,你很快就会成为表演大师。

标签: c# performance for-loop dynamic-programming


【解决方案1】:

对于初学者来说,你是在一个紧密的循环中进行字符串连接 - 不好,请查看 StringBuilder。

其次,您将在时间安排中包括设置您的测试场景。

第三,目前尚不清楚 for 循环的处理与 ProcessWidget() 方法的成本之间的相对成本是多少。肯定会在此方法中进行任何优化吗?

向下滚动查看ProcessWidget() 方法后,您还将受益于删除所有字符串连接并用一个大的string.Format() 调用或使用StringBuilder 替换。此外,ID 和 CreatedDateTime 看起来像不可变字段,也许值得将它们定义为 readonly 并在构造函数中预先创建字符串描述(如果 ProcessWidget() 可能被多次调用)。

【讨论】:

    【解决方案2】:

    您也可以考虑使用:

    widgets.Cast<Widget>().AsParallel().ForAll(widgetToProcess => {...});
    

    这将提高您的进程的性能,从 4 分钟到 30 秒(取决于 CPU 配置)。

    【讨论】:

      【解决方案3】:

      您可以通过使用StringBuilder 并将控制台输出移动到 ProcessWidget 调用中,然后将数组填充和处理放入 Parallel.For 总运行时间约为 7 秒

      这是修改后的代码:

      using System;
      using System.Collections.Concurrent;
      using System.Collections.Generic;
      using System.IO;
      using System.Linq;
      using System.Reflection;
      using System.Text;
      using System.Threading.Tasks;
      
      using System.Collections;
      using System.Diagnostics;
      
      namespace parallelFor
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Stopwatch timer = new Stopwatch();
                  timer.Start();
                  int size = 50000;
                  ArrayList widgets = new ArrayList(size);
      
                  Parallel.For(0, size, i =>
                  {
                      Widget w = new Widget((int)i);
                      widgets.Add(w);
                      w.ProcessWidget();
                  });
      
      
                  timer.Stop();
      
                  Console.WriteLine(timer.Elapsed);
                  Console.ReadLine();
              }
          }
      
          class Widget
          {
              public Widget()
              {
                  this.CreatedDateTime = DateTime.Now;
              }
      
              public Widget(int ID)
                  : this()
              {
                  this.ID = ID;
              }
      
              public DateTime CreatedDateTime { get; set; }
              public int ID { get; set; }
      
              public void ProcessWidget()
              {
              int Z = this.ID + this.ID;
              string Message = "Widget ID " + this.ID;
              Message += " was created on ";
              Message += CreatedDateTime.Year + "-" + CreatedDateTime.Month + "-" + CreatedDateTime.Day + ", at ";
              Message += CreatedDateTime.Hour + ":" + CreatedDateTime.Minute + ", with a value of ";
              Message += Z.ToString();
              Console.WriteLine(Message);
              }
          }
      }
      

      【讨论】:

      • Downvote:StringBuilder 对于较短的字符串并不快。如果您取出 Console.WriteLine 并将迭代次数增加到 2,000,000 次,那么短字符串的直接字符串连接速度会快 15%。
      • @Loathing 是的,当仅连接少量字符串时,使用 + 而不是产生 StringBuilder 对象创建成本会更快。鉴于字符串连接中有 ~10 +,我开始使用 StringBuilder。我进行了分析以验证在这种情况下串联速度更快,并更新了我的答案以反映您的反馈。感谢您指出这一点。
      • 您已从他的示例中删除了功能,所有小部件的结果不再合并。
      猜你喜欢
      • 2019-05-15
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 2020-01-16
      相关资源
      最近更新 更多