/// <summary> /// Initialize shared channel. /// </summary> /// <param name="mlosContext">Mlos context instance.</param> public void InitializeSharedChannel(MlosContext mlosContext) { // #TODO, implement experiment class. // MlosContext.Instance = mlosContext; this.mlosContext = mlosContext; // Initialize callbacks. // MlosProxyInternal.RegisterSettingsAssemblyRequestMessage.Callback = RegisterSettingsAssemblyCallback; MlosProxyInternal.RegisterSharedConfigMemoryRegionRequestMessage.Callback = RegisterSharedConfigMemoryRegionRequestMessageCallback; MlosProxy.TerminateReaderThreadRequestMessage.Callback = TerminateReaderThreadRequestMessageCallback; // Register Mlos.Core assembly. // RegisterAssembly(typeof(MlosContext).Assembly, dispatchTableBaseIndex: 0); // Register shared config memory regions. // for (uint index = 1; index < mlosContext.GlobalMemoryRegion.TotalMemoryRegionCount; index++) { // Skip first one as this is global memory region, and it does not require registration. // RegisterSharedConfigMemoryRegion(sharedMemoryRegionIndex: index + 1); } // Register assemblies from the shared config. // Assembly Mlos.NetCore does not have a config, as it is always registered first. // for (uint index = 1; index < mlosContext.GlobalMemoryRegion.RegisteredSettingsAssemblyCount.Load(); index++) { RegisterSettingsAssembly(assemblyIndex: index); } }
/// <summary> /// Initialize shared channel. /// </summary> /// <param name="mlosContext">Mlos context instance.</param> public void InitializeSharedChannel(MlosContext mlosContext) { this.mlosContext = mlosContext; // Set SharedConfig memory region. // sharedConfigManager.SetMemoryRegion(new MlosProxyInternal.SharedConfigMemoryRegion { Buffer = mlosContext.SharedConfigMemoryRegion.Buffer }); // Setup MlosContext. // MlosContext.SharedConfigManager = sharedConfigManager; // Initialize callbacks. // MlosProxyInternal.RegisterAssemblyRequestMessage.Callback = RegisterAssemblyCallback; MlosProxyInternal.RegisterMemoryRegionRequestMessage.Callback = RegisterMemoryRegionMessageCallback; MlosProxyInternal.RegisterSharedConfigMemoryRegionRequestMessage.Callback = RegisterSharedConfigMemoryRegionRequestMessageCallback; MlosProxy.TerminateReaderThreadRequestMessage.Callback = TerminateReaderThreadRequestMessageCallback; // Register Mlos.Core assembly. // RegisterAssembly(typeof(MlosContext).Assembly, dispatchTableBaseIndex: 0); // Register assemblies from the shared config. // Assembly Mlos.NetCore does not have a config, as it is always registered first. // for (uint index = 1; index < mlosContext.GlobalMemoryRegion.RegisteredSettingsAssemblyCount.Load(); index++) { RegisterSettingsAssembly(assemblyIndex: index); } }
protected virtual void Dispose(bool disposing) { if (isDisposed || !disposing) { return; } // Dispose MlosContext. // mlosContext?.Dispose(); mlosContext = null; isDisposed = true; }
/// <summary> /// Initializes a new instance of the <see cref="ExperimentSession"/> class. /// </summary> /// <param name="mlosContext"></param> public ExperimentSession(MlosContext mlosContext) { this.mlosContext = mlosContext; }
/// <summary> /// The main external agent server. /// </summary> /// <param name="args">command line arguments.</param> public static void Main(string[] args) { string executableFilePath = null; Uri optimizerAddressUri = null; CliOptionsParser.ParseArgs(args, out executableFilePath, out optimizerAddressUri); // Check for the executable before setting up any shared memory to // reduce cleanup issues. // if (executableFilePath != null && !File.Exists(executableFilePath)) { throw new FileNotFoundException($"ERROR: --executable '{executableFilePath}' does not exist."); } Console.WriteLine("Mlos.Agent.Server"); TargetProcessManager targetProcessManager = null; // Connect to gRpc optimizer only if user provided an address in the command line. // if (optimizerAddressUri != null) { Console.WriteLine("Connecting to the Mlos.Optimizer"); // This switch must be set before creating the GrpcChannel/HttpClient. // AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); // This populates a variable for the various settings registry // callback handlers to use (by means of their individual // AssemblyInitializers) to know how they can connect with the // optimizer. // // See Also: AssemblyInitializer.cs within the SettingsRegistry // assembly project in question. // MlosContext.OptimizerFactory = new MlosOptimizer.BayesianOptimizerFactory(optimizerAddressUri); } // In the active learning mode, create a new shared memory map before running the target process. // On Linux, we unlink existing shared memory map, if they exist. // If the agent is not in the active learning mode, create new or open existing to communicate with the target process. // using MlosContext mlosContext = (executableFilePath != null) ? InterProcessMlosContext.Create() : InterProcessMlosContext.CreateOrOpen(); using var mainAgent = new MainAgent(); mainAgent.InitializeSharedChannel(mlosContext); // Active learning mode. // // TODO: In active learning mode the MlosAgentServer can control the // workload against the target component. // if (executableFilePath != null) { Console.WriteLine($"Starting {executableFilePath}"); targetProcessManager = new TargetProcessManager(executableFilePath: executableFilePath); targetProcessManager.StartTargetProcess(); } else { Console.WriteLine("No executable given to launch. Will wait for agent to connect independently."); } var cancellationTokenSource = new CancellationTokenSource(); Task grpcServerTask = CreateHostBuilder(Array.Empty <string>()).Build().RunAsync(cancellationTokenSource.Token); // Start the MainAgent message processing loop as a background thread. // // In MainAgent.RunAgent we loop on the shared memory control and // telemetry channels looking for messages and dispatching them to // their registered callback handlers. // // The set of recognized messages is dynamically registered using // the RegisterSettingsAssembly method which is called through the // handler for the RegisterAssemblyRequestMessage. // // Once registered, the SettingsAssemblyManager uses reflection to // search for an AssemblyInitializer inside those assemblies and // executes it in order to setup the message handler callbacks // within the agent. // // See Also: AssemblyInitializer.cs within the SettingsRegistry // assembly project in question. // Console.WriteLine("Starting Mlos.Agent"); Task mlosAgentTask = Task.Factory.StartNew( () => mainAgent.RunAgent(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current); Task waitForTargetProcessTask = Task.Factory.StartNew( () => { if (targetProcessManager != null) { targetProcessManager.WaitForTargetProcessToExit(); targetProcessManager.Dispose(); mainAgent.UninitializeSharedChannel(); } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current); Console.WriteLine("Waiting for Mlos.Agent to exit"); while (true) { Task.WaitAny(mlosAgentTask, waitForTargetProcessTask); if (mlosAgentTask.IsFaulted && targetProcessManager != null && !waitForTargetProcessTask.IsCompleted) { // MlosAgentTask has failed, however the target process is still active. // Terminate the target process and continue shutdown. // targetProcessManager.TerminateTargetProcess(); continue; } if (mlosAgentTask.IsCompleted && waitForTargetProcessTask.IsCompleted) { // MlosAgentTask is no longer processing messages, and target process does no longer exist. // Shutdown the agent. // break; } } // Print any exceptions if occurred. // if (mlosAgentTask.Exception != null) { Console.WriteLine($"Exception: {mlosAgentTask.Exception}"); } if (waitForTargetProcessTask.Exception != null) { Console.WriteLine($"Exception: {waitForTargetProcessTask.Exception}"); } // Perform some cleanup. // waitForTargetProcessTask.Dispose(); mlosAgentTask.Dispose(); targetProcessManager?.Dispose(); cancellationTokenSource.Cancel(); grpcServerTask.Wait(); grpcServerTask.Dispose(); cancellationTokenSource.Dispose(); Console.WriteLine("Mlos.Agent exited."); }
/// <summary> /// Initializes a new instance of the <see cref="SmartCacheExperimentSession"/> class. /// This is the entry point for setting up the message handlers for the /// messages code generated from the (partial) structs defined for this /// smart component in the CodeGen/SmartCache.cs. /// </summary> /// <remarks> /// See class comments for further details. /// </remarks> /// <param name="mlosContext"></param> public SmartCacheExperimentSession(MlosContext mlosContext) : base(mlosContext) { // Setup message callbacks. // // Note: these message properties are code generated from the // (partial) structs in CodeGen/SmartCache.cs // // See out/Mlos.CodeGen.out/SmartCache/*.cs for the C# code // generation output from those partial definitions. // SmartCacheProxy.CacheRequestEventMessage.Callback = CacheRequestEventMessageHandler; SmartCacheProxy.RequestNewConfigurationMessage.Callback = RequestNewConfigurationMessageHandler; // Create smart cache parameter search space. // // These hypergrids define the combination of valid ranges // of values for the different tunables. // Note that some of these are interdependent. // // TODO: Eventually this will also be code generated from additional // "domain range" attributes on the "ScalarSettings" defined for the // component (see also CodeGen/SmartCache.cs) // Hypergrid cacheSearchSpace = new Hypergrid( name: "smart_cache_config", dimension: new CategoricalDimension("cache_implementation", CacheEvictionPolicy.LeastRecentlyUsed, CacheEvictionPolicy.MostRecentlyUsed)) .Join( subgrid: new Hypergrid( name: "lru_cache_config", dimension: new DiscreteDimension("cache_size", min: 1, max: 1 << 12)), onExternalDimension: new CategoricalDimension("cache_implementation", CacheEvictionPolicy.LeastRecentlyUsed)) .Join( subgrid: new Hypergrid( name: "mru_cache_config", dimension: new DiscreteDimension("cache_size", min: 1, max: 1 << 12)), onExternalDimension: new CategoricalDimension("cache_implementation", CacheEvictionPolicy.MostRecentlyUsed)); // Create optimization problem. // // Here we declare to the optimizer what our desired output from the // component to optimize is. // // In this case we declare "hit rate", which will be calculated as a // percentage, is the thing we want the optimizer to improve. // var optimizationProblem = new OptimizationProblem { ParameterSpace = cacheSearchSpace, ContextSpace = null, ObjectiveSpace = new Hypergrid( name: "objectives", dimensions: new ContinuousDimension(name: "HitRate", min: 0.0, max: 1.0)), }; // Define optimization objective. // optimizationProblem.Objectives.Add( new OptimizationObjective { // Tell the optimizer that we want to maximize hit rate. // Name = "HitRate", Minimize = false, }); // Get a local reference to the optimizer to reuse when processing messages later on. // // Note: we read this from a global variable that should have been // setup for the Mlos.Agent (e.g. in the Mlos.Agent.Server). // IOptimizerFactory optimizerFactory = MlosContext.OptimizerFactory; optimizerProxy = optimizerFactory?.CreateRemoteOptimizer(optimizationProblem: optimizationProblem); }
/// <summary> /// The main external agent server. /// </summary> /// <param name="args">command line arguments.</param> /// <returns>Returns exit code.</returns> public static int Main(string[] args) { CliOptionsParser.CliOptions parserOptions = CliOptionsParser.ParseArgs(args); if (!string.IsNullOrEmpty(parserOptions.ExperimentFilePath) && !File.Exists(parserOptions.ExperimentFilePath)) { throw new FileNotFoundException($"ERROR: --experiment '{parserOptions.ExperimentFilePath}' does not exist."); } if (!string.IsNullOrEmpty(parserOptions.SettingsRegistryPath)) { // #TODO temporary hack // Environment.SetEnvironmentVariable("MLOS_SETTINGS_REGISTRY_PATH", parserOptions.SettingsRegistryPath); } Console.WriteLine("Mlos.Agent.Server"); // Active learning mode. // // TODO: In active learning mode the MlosAgentServer can control the // workload against the target component. // TargetProcessManager targetProcessManager = null; if (parserOptions.Executable != null) { Console.WriteLine($"Starting: '{parserOptions.Executable}'"); targetProcessManager = new TargetProcessManager(executableFilePath: parserOptions.Executable); targetProcessManager.StartTargetProcess(); Console.WriteLine($"Launched process: '{Path.GetFileName(parserOptions.Executable)}'"); } else { Console.WriteLine("No executable given to launch. Will wait for agent to connect independently."); } // Create a Mlos context. // using MlosContext mlosContext = MlosContextFactory.Create(); // Connect to gRpc optimizer only if user provided an address in the command line. // if (parserOptions.OptimizerUri != null) { Console.WriteLine("Connecting to the Mlos.Optimizer"); // This switch must be set before creating the GrpcChannel/HttpClient. // AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); // This populates a variable for the various settings registry // callback handlers to use (by means of their individual // ExperimentSession instances) to know how they can connect with // the optimizer. // // See SmartCacheExperimentSession.cs for an example. // mlosContext.OptimizerFactory = new MlosOptimizer.BayesianOptimizerFactory(parserOptions.OptimizerUri); } var experimentSessionManager = new ExperimentSessionManager(mlosContext); using var mainAgent = new MainAgent(); mainAgent.InitializeSharedChannel(mlosContext); // If specified, load the experiment assembly. // if (!string.IsNullOrEmpty(parserOptions.ExperimentFilePath)) { experimentSessionManager.LoadExperiment(parserOptions.ExperimentFilePath); } var cancellationTokenSource = new CancellationTokenSource(); Task grpcServerTask = CreateHostBuilder(Array.Empty <string>()).Build().RunAsync(cancellationTokenSource.Token); // Start the MainAgent message processing loop as a background thread. // // In MainAgent.RunAgent we loop on the shared memory control and // telemetry channels looking for messages and dispatching them to // their registered callback handlers. // // The set of recognized messages is dynamically registered using // the RegisterSettingsAssembly method which is called through the // handler for the RegisterAssemblyRequestMessage. // // Once registered, the ExperimentSessionManager can creates an // instance of the requested ExperimentSession in order to setup the // message handler callbacks for the components messages within the // agent. // // See SmartCacheExperimentSession.cs for an example. // Console.WriteLine("Starting Mlos.Agent"); Task mlosAgentTask = Task.Factory.StartNew( () => mainAgent.RunAgent(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current); Task waitForTargetProcessTask = Task.Factory.StartNew( () => { if (targetProcessManager != null) { targetProcessManager.WaitForTargetProcessToExit(); targetProcessManager.Dispose(); mainAgent.UninitializeSharedChannel(); } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current); Console.WriteLine("Waiting for Mlos.Agent to exit"); while (true) { Task.WaitAny(mlosAgentTask, waitForTargetProcessTask); if (mlosAgentTask.IsFaulted && targetProcessManager != null && !waitForTargetProcessTask.IsCompleted) { // MlosAgentTask has failed, however the target process is still active. // Terminate the target process and continue shutdown. // targetProcessManager.TerminateTargetProcess(); continue; } if (mlosAgentTask.IsCompleted && waitForTargetProcessTask.IsCompleted) { // MlosAgentTask is no longer processing messages, and target process does no longer exist. // Shutdown the agent. // break; } } int exitCode = 0; // Print any exceptions if occurred. // if (mlosAgentTask.Exception != null) { Console.WriteLine($"Exception: {mlosAgentTask.Exception}"); exitCode |= 1; } if (waitForTargetProcessTask.Exception != null) { Console.WriteLine($"Exception: {waitForTargetProcessTask.Exception}"); exitCode |= 2; } // Perform some cleanup. // waitForTargetProcessTask.Dispose(); mlosAgentTask.Dispose(); targetProcessManager?.Dispose(); cancellationTokenSource.Cancel(); grpcServerTask.Wait(); grpcServerTask.Dispose(); cancellationTokenSource.Dispose(); Console.WriteLine("Mlos.Agent exited."); return(exitCode); }