//******************************************************************************************************
// 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.");
}
}
}
}