为什么我的C#程序分析器更快? [英] Why is my C# program faster in a profiler?

查看:156
本文介绍了为什么我的C#程序分析器更快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

予具有相对大的系统(〜25000线平分)用于监视无线电相关的装置。它显示图形和ZedGraph的这种使用最新版本。 该方案是用C#在VS2010与Win7的codeD。 现在的问题是:

I have a relatively large system (~25000 lines so far) for monitoring radio-related devices. It shows graphs and such using latest version of ZedGraph. The program is coded using C# on VS2010 with Win7. The problem is:

  • 当我从内VS运行程序,它运行速度慢
  • 当我运行内置的EXE程序,它运行速度慢
  • 当我运行该程序,虽然性能向导/ CPU事件探查器,运行速度极快。
  • 当我运行内置的EXE程序,然后再启动VS和附加探查到任何其他进程,我的程序加快了!

我想要的程序始终运行那么快!

I want the program to always run that fast!

在解决方案中每个项目都将发布,调试非托管code是禁用,定义调试和跟踪常量被禁用,优化code - 我想要么,警告级别 - 我想要么,燮preSS JIT - 我想要么, 总之我尝试了所有已经提出了计算器的解决方案 - 没有工作。程序很慢外廓,快探查。 我不认为这个问题是在我的code,因为如果我装上探查到其他不相关的进程和变快!

Every project in the solution is set to RELEASE, Debug unmanaged code is DISABLED, Define DEBUG and TRACE constants is DISABLED, Optimize Code - I tried either, Warning Level - I tried either, Suppress JIT - I tried either, in short I tried all the solutions already proposed on StackOverflow - none worked. Program is slow outside profiler, fast in profiler. I don't think the problem is in my code, because it becomes fast if I attach the profiler to other, unrelated process as well!

请帮忙! 我真的需要它是那么快无处不在,因为它是一个关键业务应用和性能问题不能耐受...

Please help! I really need it to be that fast everywhere, because it's a business critical application and performance issues are not tolerated...

更新1 - 8的后续

-------------------- UPDATE1:--------------------

这个问题似乎不被ZedGraph相关的,因为它仍然表现后,我换成ZedGraph用我自己的基本绘图。

The problem seems to Not be ZedGraph related, because it still manifests after I replaced ZedGraph with my own basic drawing.

-------------------- UPDATE2:--------------------

在虚拟机中运行该程序,该程序还在运行速度慢,并且从主机上运行探查并不能使它快。

Running the program in a Virtual machine, the program still runs slow, and running profiler from the Host machine doesn't make it fast.

-------------------- UPDATE3:--------------------

开始屏幕捕获视频也加快了程序了!

Starting screen capture to video also speeds the program up!

-------------------- UPDATE4:--------------------

如果我打开英特尔图形驱动程序设置窗口(这件事: HTTP ://www.intel.com/support/graphics/sb/img/resolution_new.jpg ) 而只是不断地用光标键上悬停,所以他们发光,等我的程序加快! 它不会加快,如果我跑GPUZ或Kombustor了,所以在GPU上没有downclocking - 它保持稳定的850MHz

If I open the Intel graphics driver settings window (this thing: http://www.intel.com/support/graphics/sb/img/resolution_new.jpg) and just constantly hover with the cursor over buttons, so they glow, etc, my program speeds up!. It doesn't speed up if I run GPUz or Kombustor though, so no downclocking on the GPU - it stays steady 850Mhz.

-------------------- Update5:--------------------

在不同的机器测试:

-On我的酷睿i5-2400S与英特尔HD2000,UI的运行速度缓慢,CPU占用率约15%。

-On my Core i5-2400S with Intel HD2000, UI runs slow and CPU usage is ~15%.

-On同事的Core 2 Duo处理器采用Intel G41防爆preSS,UI运行速度快,而且CPU占用率〜90%(这是不正常的两种)

-On a colleague's Core 2 Duo with Intel G41 Express, UI runs fast, but CPU usage is ~90% (which isn't normal either)

-On睿i5-2400S专用的Radeon X1650,UI的运行速度超快,CPU使用率〜50%。

-On Core i5-2400S with dedicated Radeon X1650, UI runs blazing fast, CPU usage is ~50%.

-------------------- Update6:--------------------

的code剪断显示我如何更新一个图( graphFFT ZedGraphControl 的封装为方便使用):

A snip of code showing how I update a single graph (graphFFT is an encapsulation of ZedGraphControl for ease of use):

public void LoopDataRefresh() //executes in a new thread
        {
            while (true)
            {
                while (!d.Connected)
                    Thread.Sleep(1000);
                if (IsDisposed)
                    return;
//... other graphs update here
                if (signalNewFFT && PanelFFT.Visible)
                {
                    signalNewFFT = false;
                    #region FFT
                    bool newRange = false;
                    if (graphFFT.MaxY != d.fftRangeYMax)
                    {
                        graphFFT.MaxY = d.fftRangeYMax;
                        newRange = true;
                    }
                    if (graphFFT.MinY != d.fftRangeYMin)
                    {
                        graphFFT.MinY = d.fftRangeYMin;
                        newRange = true;
                    }

                    List<PointF> points = new List<PointF>(2048);
                    int tempLength = 0;
                    short[] tempData = new short[2048];

                    int i = 0;

                    lock (d.fftDataLock)
                    {
                        tempLength = d.fftLength;
                        tempData = (short[])d.fftData.Clone();
                    }
                    foreach (short s in tempData)
                        points.Add(new PointF(i++, s));

                    graphFFT.SetLine("FFT", points);

                    if (newRange)
                        graphFFT.RefreshGraphComplete();
                    else if (PanelFFT.Visible)
                        graphFFT.RefreshGraph();

                    #endregion
                }
//... other graphs update here
                Thread.Sleep(5);
            }
        }

的setLine 是:

public void SetLine(String lineTitle, List<PointF> values)
    {
        IPointListEdit ip = zgcGraph.GraphPane.CurveList[lineTitle].Points as IPointListEdit;
        int tmp = Math.Min(ip.Count, values.Count);
        int i = 0;
        while(i < tmp)
        {
            if (values[i].X > peakX)
                peakX = values[i].X;
            if (values[i].Y > peakY)
                peakY = values[i].Y;
            ip[i].X = values[i].X;
            ip[i].Y = values[i].Y;
            i++;
        }
        while(ip.Count < values.Count)
        {
            if (values[i].X > peakX)
                peakX = values[i].X;
            if (values[i].Y > peakY)
                peakY = values[i].Y;
            ip.Add(values[i].X, values[i].Y);
            i++;
        }
        while(values.Count > ip.Count)
        {
            ip.RemoveAt(ip.Count - 1);
        }
    }

RefreshGraph 是:

public void RefreshGraph()
    {
        if (!explicidX && autoScrollFlag)
        {
            zgcGraph.GraphPane.XAxis.Scale.Max = Math.Max(peakX + grace.X, rangeX);
            zgcGraph.GraphPane.XAxis.Scale.Min = zgcGraph.GraphPane.XAxis.Scale.Max - rangeX;
        }
        if (!explicidY)
        {
            zgcGraph.GraphPane.YAxis.Scale.Max = Math.Max(peakY + grace.Y, maxY);
            zgcGraph.GraphPane.YAxis.Scale.Min = minY;
        }
        zgcGraph.Refresh();
    }

--------------------的Update7:--------------------

刚跑到它通过蚂蚁探查。它告诉我, ZedGraph 刷新计数当程序快速进行的 precisely 的两倍高时相比,它的速度慢。 以下是截图:

Just ran it through the ANTS profiler. It tells me that the ZedGraph refresh counts when the program is fast are precisely two times higher compared to when it's slow. Here are the screenshots:

我觉得很奇怪的是,考虑到部分的长度差异小,性能与数学precision相差两倍。

I find it VERY strange that, considering the small difference in the length of the sections, performance differs twice with mathematical precision.

另外,我更新了GPU的驱动程序,并没有帮助。

Also, I updated the GPU driver, that didn't help.

-------------------- Update8:--------------------

不幸的是,现在过了几天,我无法重现该问题...我得到恒定的可接受的速度(这似乎仍然比我曾在探查两周前有点慢)的ISN牛逼受到任何的是用来影响其两周前的因素 - 剖析器,视频捕获或GPU驱动程序窗口。我仍然没有解释是什么导致它...

Unfortunately, for a few days now, I'm unable to reproduce the issue... I'm getting constant acceptable speed (which still appear a bit slower than what I had in the profiler two weeks ago) which isn't affected by any of the factors that used to affect it two weeks ago - profiler, video capturing or GPU driver window. I still have no explanation of what was causing it...

推荐答案

有情况减速,当一个线程可以显著加快其他线程,通常当一个线程轮询或经常锁定了一些公共资源。

There are situations when slowing down a thread can speed up other threads significantly, usually when one thread is polling or locking some common resource frequently.

有关实例(这是Windows表单为例)当主线程检查紧密循环的整体进步,而不是使用定时器,例如:

For instance (this is a windows-forms example) when the main thread is checking overall progress in a tight loop instead of using a timer, for example:

private void SomeWork() {
  // start the worker thread here
  while(!PollDone()) {
    progressBar1.Value = PollProgress();
    Application.DoEvents(); // keep the GUI responisive
  }
}

减缓下来可以提高性能:

Slowing it down could improve performance:

private void SomeWork() {
  // start the worker thread here
  while(!PollDone()) {
    progressBar1.Value = PollProgress();
    System.Threading.Thread.Sleep(300); // give the polled thread some time to work instead of responding to your poll
    Application.DoEvents(); // keep the GUI responisive
  }
}

这样做是正确的,应该避免使用的DoEvents调用总数:

Doing it correctly, one should avoid using the DoEvents call alltogether:

private Timer tim = new Timer(){ Interval=300 };

private void SomeWork() {
  // start the worker thread here
  tim.Tick += tim_Tick;
  tim.Start();
}

private void  tim_Tick(object sender, EventArgs e){
  tim.Enabled = false; // prevent timer messages from piling up
  if(PollDone()){
    tim.Tick -= tim_Tick;
    return;
  }
  progressBar1.Value = PollProgress();
  tim.Enabled = true;
}

调用 Application.DoEvents()有可能导致配发头痛时GUI的东西还没有被禁用,用户揭开序幕其他事件或同一事件同时是第二次造成这大自然排队背后的新的第一个动作堆栈攀升,但是我会题外话。

Calling Application.DoEvents() can potentially cause allot of headaches when GUI stuff has not been disabled and the user kicks off other events or the same event a 2nd time simultaneously, causing stack climbs which by nature queue the first action behind the new one, but I'm going off topic.

可能是例子太具体的WinForms,我会尽力做一个更普遍的例子。如果你有一个线程是填补由其他线程处理缓冲区,一定要留一些 System.Threading.Thread.Sleep()松弛循环,使其他线程做一些处理,检查前如果缓冲区需要再次填充:

Probably that example is too winforms specific, I'll try making a more general example. If you have a thread that is filling a buffer that is processed by other threads, be sure to leave some System.Threading.Thread.Sleep() slack in the loop to allow the other threads to do some processing before checking if the buffer needs to be filled again:

public class WorkItem { 
  // populate with something usefull
}

public static object WorkItemsSyncRoot = new object();
public static Queue<WorkItem> workitems = new Queue<WorkItem>();

public void FillBuffer() {
  while(!done) {
    lock(WorkItemsSyncRoot) {
      if(workitems.Count < 30) {
        workitems.Enqueue(new WorkItem(/* load a file or something */ ));
      }
    }
  }
}

工作线程的将有困难,因为其经常被锁定的填充线,以获得从队列任何东西。添加一个睡眠()(锁之外)能显著加快其他线程:

The worker thread's will have difficulty to obtain anything from the queue since its constantly being locked by the filling thread. Adding a Sleep() (outside the lock) could significantly speed up other threads:

public void FillBuffer() {
  while(!done) {
    lock(WorkItemsSyncRoot) {
      if(workitems.Count < 30) {
        workitems.Enqueue(new WorkItem(/* load a file or something */ ));
      }
    }
    System.Threading.Thread.Sleep(50);
  }
}

挂钩探查可能在某些情况下具有相同的效果睡眠功能。

Hooking up a profiler could in some cases have the same effect as the sleep function.

我不知道,如果我给重新presentative的例子(这是相当难以拿出一些简单的),但我猜的一点是清楚的,把睡眠()在正确的位置可以帮助提高流动其他线程。

I'm not sure if I've given representative examples (it's quite hard to come up with something simple) but I guess the point is clear, putting sleep() in the correct place can help improve the flow of other threads.

----------的Update7后编辑-------------

---------- Edit after Update7 -------------

我会完全删除 LoopDataRefresh()线程。而把定时器在窗口具有至少20的间隔(这将是50帧每秒,如果没有被跳过):

I'd remove that LoopDataRefresh() thread altogether. Rather put a timer in your window with an interval of at least 20 (which would be 50 frames a second if none were skipped):

private void tim_Tick(object sender, EventArgs e) {
  tim.Enabled = false; // skip frames that come while we're still drawing
  if(IsDisposed) {
    tim.Tick -= tim_Tick;
    return;
  }

  // Your code follows, I've tried to optimize it here and there, but no guarantee that it compiles or works, not tested at all

  if(signalNewFFT && PanelFFT.Visible) {
    signalNewFFT = false;

    #region FFT
    bool newRange = false;
    if(graphFFT.MaxY != d.fftRangeYMax) {
      graphFFT.MaxY = d.fftRangeYMax;
      newRange = true;
    }
    if(graphFFT.MinY != d.fftRangeYMin) {
      graphFFT.MinY = d.fftRangeYMin;
      newRange = true;
    }

    int tempLength = 0;
    short[] tempData;

    int i = 0;

    lock(d.fftDataLock) {
      tempLength = d.fftLength;
      tempData = (short[])d.fftData.Clone();
    }

    graphFFT.SetLine("FFT", tempData);

    if(newRange) graphFFT.RefreshGraphComplete();
    else if(PanelFFT.Visible) graphFFT.RefreshGraph();
    #endregion

    // End of your code

    tim.Enabled = true; // Drawing is done, allow new frames to come in.
  }
}

下面是优化的setLine(),它不再需要点的名单,但原始数据:

Here's the optimized SetLine() which no longer takes a list of points but the raw data:

public class GraphFFT {
    public void SetLine(String lineTitle, short[] values) {
      IPointListEdit ip = zgcGraph.GraphPane.CurveList[lineTitle].Points as IPointListEdit;
      int tmp = Math.Min(ip.Count, values.Length);
      int i = 0;
      peakX = values.Length;

      while(i < tmp) {
        if(values[i] > peakY) peakY = values[i];
        ip[i].X = i;
        ip[i].Y = values[i];
        i++;
      }
      while(ip.Count < values.Count) {
        if(values[i] > peakY) peakY = values[i];
        ip.Add(i, values[i]);
        i++;
      }
      while(values.Count > ip.Count) {
        ip.RemoveAt(ip.Count - 1);
      }
    }
  }

我希望你得到的工作,因为我以前评论,我hav'nt了编译或检查它,可能会有一些错误出现的机会。还有更多的优化有,但比起跳帧,只有收集数据的时候,我们有足够的时间来实际绘制帧之前下一个进来的提振优化应该是微不足道的。

I hope you get that working, as I commented before, I hav'nt got the chance to compile or check it so there could be some bugs there. There's more to be optimized there, but the optimizations should be marginal compared to the boost of skipping frames and only collecting data when we have the time to actually draw the frame before the next one comes in.

如果您密切关注 iZotope 学习曲线的视频,你会请注意,他们也跳帧,有时候都有点神经质。这不是坏的,这是一个权衡你做的前台线程的处理能力和背景工人之间。

If you closely study the graphs in the video at iZotope, you'll notice that they too are skipping frames, and sometimes are a bit jumpy. That's not bad at all, it's a trade-off you make between the processing power of the foreground thread and the background workers.

如果你真的想图纸在一个单独的线程来完成,你必须将图形绘制到一个位图(调用抽奖(),并传递位图设备上下文)。然后通过位图到主线程,并将它更新。这样,你失去的设计师和属性网格在IDE的方便,但你可以使用,否则空置内核的处理器。

If you really want the drawing to be done in a separate thread, you'll have to draw the graph to a bitmap (calling Draw() and passing the bitmaps device context). Then pass the bitmap on to the main thread and have it update. That way you do lose the convenience of the designer and property grid in your IDE, but you can make use of otherwise vacant processor cores.

----------编辑答案言论--------

---------- edit answer to remarks --------

是有办法告诉什么叫什么。看看你的第一个屏幕截图,选择了调用树图。每下一行跳跃了一下(这是一个树状视图,而不仅仅是一个清单!)。在调用图,每个树节点重新presents一个已被称为它的父树节点(方法)方法。

Yes there is a way to tell what calls what. Look at your first screen-shot, you have selected the "call tree" graph. Each next line jumps in a bit (it's a tree-view, not just a list!). In a call-graph, each tree-node represents a method that has been called by its parent tree-node (method).

在第一幅图像,的WndProc 被称为约1800倍,它处理872的消息,其中62触发 ZedGraphControl.OnPaint()(这反过来又占主要线程的53%,总时间)。

In the first image, WndProc was called about 1800 times, it handled 872 messages of which 62 triggered ZedGraphControl.OnPaint() (which in turn accounts for 53% of the main threads total time).

你没有看到另一个的RootNode究其原因,是因为第三下拉框中选择了[604]绵线程这是我之前没有注意到。

The reason you don't see another rootnode, is because the 3rd dropdown box has selected "[604] Mian Thread" which I didn't notice before.

对于更流畅的图形,我有这第二次的想法后,现在正在寻找更加紧密的屏幕截图。主线程显然已经收到了(双)更新消息,并且CPU还是有一定的上升空间。

As for the more fluent graphs, I have 2nd thoughts on that now after looking more closely to the screen-shots. The main thread has clearly received more (double) update messages, and the CPU still has some headroom.

它看起来像线程超出同步,并同步在不同的时间,更新报文到达那里只是为时已晚的时间(时的WndProc做睡觉去了一会儿),然后突然对一下子。我不是很熟悉的蚂蚁,但它有一个并排线时间表包括睡眠时间?你应该能够看到在这种观点怎么回事。微软线程查看工具会来方便了这一点:

It looks like the threads are out-of-sync and in-sync at different times, where the update messages arrive just too late (when WndProc was done and went to sleep for a while), and then suddenly in time for a while. I'm not very familiar with Ants, but does it have a side-by side thread timeline including sleep time? You should be able to see what's going on in such a view. Microsofts threads view tool would come in handy for this:

这篇关于为什么我的C#程序分析器更快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆