//****************************************************************************************************** // OptimizationOptions.cs - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 10/27/2016 - Steven E. Chisholm // Generated original version of source code. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Diagnostics; using GSF.Configuration; using GSF.Diagnostics; namespace GSF { /// /// This class will contain various optimizations that can be enabled in certain circumstances /// through the SystemSettings. Since this framework is used in many settings, for stability /// reasons, tradeoffs are made. This gives the users opportunities to enable/disable certain /// optimizations if for some reason they cause adverse effects on their system. /// public static class OptimizationOptions { /// /// The routing method to use. /// public enum RoutingMethod { /// /// The default method of routing /// Default, /// /// A custom implementation that sacrifices overall latency for lower CPU utilization. /// HighLatencyLowCpu, } private static readonly LogPublisher Log = Logger.CreatePublisher(typeof(OptimizationOptions), MessageClass.Framework); /// /// Eliminates certain async queues in the phasor protocol parsing. /// public static bool DisableAsyncQueueInProtocolParsing { get; private set; } = false; /// /// Uses dedicated threads instead of LongSynchronizedOperations in certain cases. /// public static bool PreferDedicatedThreads { get; private set; } = false; /// /// Specifies the desired routing method. /// public static RoutingMethod DefaultRoutingMethod { get; private set; } = RoutingMethod.Default; /// /// Specifies a routing latency if the routing method recgonizes this. /// public static int RoutingLatency { get; private set; } = 50; /// /// Specifies the target number of measurements per batch operation. /// public static int RoutingBatchSize { get; private set; } = 200; /// /// Specifies that threadpool monitoring will be enabled. /// public static bool EnableThreadPoolMonitoring { get; private set; } = false; /// /// Specifies that the threadpool monitor should also dump the stack trace of all thread. /// public static bool EnableThreadStackDumping { get; private set; } = false; static OptimizationOptions() { string setting = string.Empty; try { ConfigurationFile configFile = ConfigurationFile.Current; CategorizedSettingsElementCollection systemSettings = configFile.Settings["systemSettings"]; systemSettings.Add("OptimizationsConnectionString", "", "Specifies which optimizations to enable for the system."); setting = systemSettings["OptimizationsConnectionString"].ValueAsString(""); Dictionary optimizations = setting.ParseKeyValuePairs(); LoadThreadPoolMonitoring(optimizations); LoadPreferDedicatedThreads(optimizations); LoadAsyncQueueInProtocolParsing(optimizations); LoadProcessorAffinity(optimizations); LoadRoutingTable(optimizations); } catch (Exception ex) { Log.Publish(MessageLevel.Warning, "Could not parse Optimization Settings", setting, null, ex); } ThreadPoolMonitor.Initialize(); } private static void LoadThreadPoolMonitoring(Dictionary optimizations) { if (optimizations.ContainsKey("EnableThreadPoolMonitoring")) { Log.Publish(MessageLevel.Info, "Enable Optimization", "EnableThreadPoolMonitoring"); EnableThreadPoolMonitoring = true; } if (optimizations.ContainsKey("EnableThreadStackDumping")) { Log.Publish(MessageLevel.Info, "Enable Optimization", "EnableThreadStackDumping"); EnableThreadStackDumping = true; } } private static void LoadAsyncQueueInProtocolParsing(Dictionary optimizations) { if (optimizations.ContainsKey("DisableAsyncQueueInProtocolParsing")) { Log.Publish(MessageLevel.Info, "Enable Optimization", "DisableAsyncQueueInProtocolParsing"); DisableAsyncQueueInProtocolParsing = true; } } private static void LoadPreferDedicatedThreads(Dictionary optimizations) { if (optimizations.ContainsKey("PreferDedicatedThreads")) { Log.Publish(MessageLevel.Info, "Enable Optimization", "PreferDedicatedThreads"); PreferDedicatedThreads = true; } } private static void LoadProcessorAffinity(Dictionary optimizations) { if (optimizations.ContainsKey("ProcessorAffinity")) { ulong value; if (ulong.TryParse(optimizations["ProcessorAffinity"], out value)) { if (value > 0) { Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)(long)value; Log.Publish(MessageLevel.Info, "Enable Optimization", "Processor Affinity set to " + Process.GetCurrentProcess().ProcessorAffinity.ToInt64().ToString("X")); } else { Log.Publish(MessageLevel.Warning, "Parsing Error", "Processor Affinity cannot be zero"); } } else { Log.Publish(MessageLevel.Warning, "Parsing Error", "Unrecognized option for ProcessAffinity: " + optimizations["ProcessorAffinity"]); } } } private static void LoadRoutingTable(Dictionary optimizations) { if (optimizations.ContainsKey("RoutingMethod")) { string method = optimizations["RoutingMethod"]; if (method.Equals("RouteMappingHighLatencyLowCpu", StringComparison.CurrentCultureIgnoreCase)) { int latency = -1; if (optimizations.ContainsKey("RoutingLatencyMS")) { string latencyString = optimizations["RoutingLatencyMS"]; if (int.TryParse(latencyString, out latency)) { if (latency < 1 || latency > 500) { Log.Publish(MessageLevel.Info, "Routing Table", "Invalid range of routing latency. Defaulting to 10 ms. (Range: 1ms to 500ms)", "Value: " + latencyString); latency = 10; } } else { Log.Publish(MessageLevel.Info, "Routing Table", "Could not parse latency value. Defaulting to 10 ms.", "Value: " + latencyString); latency = 10; } } else { latency = 10; } int countPerBatch = -1; if (optimizations.ContainsKey("RoutingBatchSize")) { string countPerBatchString = optimizations["RoutingBatchSize"]; if (int.TryParse(countPerBatchString, out countPerBatch)) { if (countPerBatch < 20 || countPerBatch > 10000) { Log.Publish(MessageLevel.Info, "Routing Table", "Invalid RoutingBatchSize. Defaulting to 200. (Range: 20 to 10000)", "Value: " + countPerBatch); countPerBatch = 200; } } else { Log.Publish(MessageLevel.Info, "Routing Table", "Could not parse RoutingBatchSize. Defaulting to 200.", "Value: " + RoutingBatchSize); countPerBatch = 200; } } else { countPerBatch = 200; } Log.Publish(MessageLevel.Info, "Routing Table", "Using RouteMappingHighLatencyLowCpu.", "Latency: " + latency.ToString() + " Batch Size: " + countPerBatch); DefaultRoutingMethod = RoutingMethod.HighLatencyLowCpu; RoutingLatency = latency; RoutingBatchSize = countPerBatch; } else if (method.Equals("Default", StringComparison.CurrentCultureIgnoreCase)) { Log.Publish(MessageLevel.Info, "Routing Table", "Using the default routing table."); } else { Log.Publish(MessageLevel.Warning, "Routing Table", "Specified routing method is not recognized. The default will be used.", "Value: " + method); } } else { Log.Publish(MessageLevel.Info, "Routing Table", "Method not specified, using the default routing table."); } } } }