Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>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.</p> <p>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:</p> <pre><code>private void SomeWork() { // start the worker thread here while(!PollDone()) { progressBar1.Value = PollProgress(); Application.DoEvents(); // keep the GUI responisive } } </code></pre> <p>Slowing it down could improve performance:</p> <pre><code>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 } } </code></pre> <p>Doing it correctly, one should avoid using the DoEvents call alltogether:</p> <pre><code>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; } </code></pre> <p>Calling <code>Application.DoEvents()</code> 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.</p> <p>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 <code>System.Threading.Thread.Sleep()</code> slack in the loop to allow the other threads to do some processing before checking if the buffer needs to be filled again:</p> <pre><code>public class WorkItem { // populate with something usefull } public static object WorkItemsSyncRoot = new object(); public static Queue&lt;WorkItem&gt; workitems = new Queue&lt;WorkItem&gt;(); public void FillBuffer() { while(!done) { lock(WorkItemsSyncRoot) { if(workitems.Count &lt; 30) { workitems.Enqueue(new WorkItem(/* load a file or something */ )); } } } } </code></pre> <p>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:</p> <pre><code>public void FillBuffer() { while(!done) { lock(WorkItemsSyncRoot) { if(workitems.Count &lt; 30) { workitems.Enqueue(new WorkItem(/* load a file or something */ )); } } System.Threading.Thread.Sleep(50); } } </code></pre> <p>Hooking up a profiler could in some cases have the same effect as the sleep function.</p> <p>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.</p> <p>---------- Edit after Update7 -------------</p> <p>I'd remove that <code>LoopDataRefresh()</code> 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):</p> <pre><code>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 &amp;&amp; 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. } } </code></pre> <p>Here's the optimized SetLine() which no longer takes a list of points but the raw data:</p> <pre><code>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 &lt; tmp) { if(values[i] &gt; peakY) peakY = values[i]; ip[i].X = i; ip[i].Y = values[i]; i++; } while(ip.Count &lt; values.Count) { if(values[i] &gt; peakY) peakY = values[i]; ip.Add(i, values[i]); i++; } while(values.Count &gt; ip.Count) { ip.RemoveAt(ip.Count - 1); } } } </code></pre> <p>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.</p> <p>If you closely study the graphs in the video at <a href="http://www.izotope.com/products/audio/insight/" rel="nofollow noreferrer">iZotope</a>, 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.</p> <p>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.</p> <p>---------- edit answer to remarks --------</p> <p>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).</p> <p>In the first image, <code>WndProc</code> was called about 1800 times, it handled 872 messages of which 62 triggered <code>ZedGraphControl.OnPaint()</code> (which in turn accounts for 53% of the main threads total time).</p> <p>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.</p> <p>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.</p> <p>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 <a href="http://msdn.microsoft.com/en-us/library/dd627193%28v=vs.110%29.aspx" rel="nofollow noreferrer">threads view tool</a> would come in handy for this: <img src="https://i.stack.imgur.com/TDCVW.png" alt="enter image description here"></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload