/// <summary> /// Helper method that chunks up the work done by the handler. /// The method returns true while there is more work to be done by the handler. /// quantum parameter determines the size of one chunck of work to be done by the handler. /// </summary> /// <param name="quantum">The current quantum allocation. Each call updates so that the handler will more likely hit the ideal time it takes for each chunck of work</param> /// <param name="handler">The handler to call that does the work being throttled</param> /// <returns>Returns true if there is more work to be done by the handler. false otherwise.</returns> private bool SelfThrottlingWorker(ref int quantum, QuantizedWorkHandler handler) { int work; int workDone; bool workRemaining = true; if (this.throttlingLimit > 0) { work = this.throttlingLimit; workDone = handler(work); } else { work = quantum; this.throttlingWorkerWatch.Restart(); workDone = handler(work); this.throttlingWorkerWatch.Stop(); long duration = this.throttlingWorkerWatch.ElapsedMilliseconds; if (workDone > 0 && duration > 0) { long adjustedQuantum = (workDone * this.idealDuration) / duration; quantum = Math.Max(this.minimumQuantum, (int)Math.Min(adjustedQuantum, int.MaxValue)); } } if (workDone < work) { workRemaining = false; } return(workRemaining); }
/// <summary> /// Helper method for self-tuning how much time is allocated to the given handler. /// </summary> /// <param name="quantum">The current quantum allocation</param> /// <param name="idealDuration">The time in milliseconds we want to take</param> /// <param name="handler">The handler to call that does the work being throttled</param> /// <returns>Returns the new quantum to use next time that will more likely hit the ideal time</returns> private static int SelfThrottlingWorker(int quantum, int idealDuration, QuantizedWorkHandler handler) { PerfTimer timer = new PerfTimer(); timer.Start(); int count = handler(quantum); timer.Stop(); long duration = timer.GetDuration(); if (duration > 0 && count > 0) { long estimatedFullDuration = duration * (quantum / count); long newQuanta = (quantum * idealDuration) / estimatedFullDuration; quantum = Math.Max(100, (int)Math.Min(newQuanta, int.MaxValue)); } return(quantum); }
/// <summary> /// Helper method for self-tuning how much time is allocated to the given handler. /// </summary> /// <param name="quantum">The current quantum allocation</param> /// <param name="idealDuration">The time in milliseconds we want to take</param> /// <param name="handler">The handler to call that does the work being throttled</param> /// <returns>Returns the new quantum to use next time that will more likely hit the ideal time</returns> private static int SelfThrottlingWorker(int quantum, int idealDuration, QuantizedWorkHandler handler) { PerfTimer timer = new PerfTimer(); timer.Start(); int count = handler(quantum); timer.Stop(); long duration = timer.GetDuration(); if (duration > 0 && count > 0) { long estimatedFullDuration = duration * (quantum / count); long newQuanta = (quantum * idealDuration) / estimatedFullDuration; quantum = Math.Max(100, (int)Math.Min(newQuanta, int.MaxValue)); } return quantum; }
/// <summary> /// Realizes and virtualizes items based on the current viewbox. /// </summary> /// <returns>An enumerator which allows this method to continue realization where it left off.</returns> private IEnumerator RealizeOverride() { IVisualFactory f = VisualFactory ?? this.defaultFactory; f.BeginRealize(); IEnumerator <ISpatialItem> itemEnumerator = null; HashSet <ISpatialItem> realizedItems = new HashSet <ISpatialItem>(); // RealizeItems if (this.Items != null) { IEnumerable <ISpatialItem> itemsToRealize = null; if (this.IsVirtualizing) { // Only realize the items within our viewbox. double scale = Scale; Rect viewbox = ActualViewbox; // Buffer the viewbox. // This just seems to make things worse, especially when zoomed out a long way. // A smarter algorithm would predict the direction we are moving and only prefetch those. // viewbox.Inflate(viewbox.Width / 2, viewbox.Height / 2); // Query the index for all items that intersect our viewbox. // use ToList() because we can't leave the query to be lazy. // by the time RealizeItems is called below the contents of the query // may have mutated. itemsToRealize = Items.GetItemsIntersecting(viewbox).ToList(); } else { // Get all items. itemsToRealize = Items; } itemEnumerator = itemsToRealize.GetEnumerator(); QuantizedWorkHandler realizeHandler = delegate(int realizationQuantum) { return(this.RealizeItems(itemEnumerator, realizedItems, realizationQuantum)); }; while (this.SelfThrottlingWorker(ref this.realizationQuantum, realizeHandler)) { yield return(true); } // Raise VisualChildrenChanged only if new if (realizedItems.Count > 0) { // Raise the VisualChildrenChanged event since all items are visible. if (this.VisualChildrenChanged != null) { this.VisualChildrenChanged(this, EventArgs.Empty); } } } // VirtualizeItems // Build a list of items to virtualize. IList <ISpatialItem> itemsToVirtualize = new List <ISpatialItem>(this.visualMap.Count); // Get any items that are no longer part of our result set. foreach (ISpatialItem item in this.visualMap.Keys) { if (!realizedItems.Contains(item)) { if (this.ShouldVirtualize(item)) { itemsToVirtualize.Add(item); } } } itemEnumerator = itemsToVirtualize.GetEnumerator(); QuantizedWorkHandler virtualizingHandler = delegate(int virtualizationQuantum) { return(this.VirtualizeItems(itemEnumerator, virtualizationQuantum)); }; while (this.SelfThrottlingWorker(ref this.virtualizationQuantum, virtualizingHandler)) { yield return(true); } f.EndRealize(); }