public async Task CouldFindMaximumWithSpinWait() { var par0 = new Parameter("par0", 0, 10, 1); var par1 = new Parameter("par1", 0, 10, 1); var par2 = new Parameter("par2", 0, 10, 1); var pars = new Parameters(new[] { par0, par1, par2 }); var total = pars.TotalInterations; Console.WriteLine($"Total iterations: {total}"); Func <Parameters, ValueTask <double> > targetFunc = (args) => { var task = Task.Run(() => { var sum = 0L; for (var i = 0; i < 10000000; i++) { sum = sum + i; } // avoid any release optimization if (sum == int.MaxValue) { throw new ApplicationException(); } var result = -(Math.Pow(2 - args[0].Current, 2) + Math.Pow(1 - args[1].Current, 2) + Math.Pow(7 - args[2].Current, 2)); return(result); }); return(new ValueTask <double>(task)); }; // true for memoize, second run is almost instant var maximizer = new GridOptimizer <double>(pars, targetFunc, true); EvalFolderFunc <double, EvalAddress> folder = FolderFunc; for (int i = 0; i < 2; i++) { var sw = new Stopwatch(); var startMemory = GC.GetTotalMemory(true); sw.Restart(); var optimum = await maximizer.FoldGrid(EvalAddress.Worst, folder); var optParams = pars.SetPositionsFromLinearAddress(optimum.LinearAddress); var endMemory = GC.GetTotalMemory(false); sw.Stop(); Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds}"); Console.WriteLine($"Memory: {endMemory - startMemory}"); Console.WriteLine($"Optimum: {optParams[0].Current} - {optParams[1].Current} - {optParams[2].Current}"); } }
public async Task CouldFindMaximum() { var sw = new Stopwatch(); var startMemory = GC.GetTotalMemory(true); sw.Restart(); var par0 = new Parameter("par0", 0, 100, 1); var par1 = new Parameter("par1", 0, 100, 1); var par2 = new Parameter("par2", 0, 100, 1); var pars = new Parameters(new[] { par0, par1, par2 }); var total = pars.TotalInterations; Console.WriteLine($"Total iterations: {total}"); Func <Parameters, ValueTask <double> > targetFunc = (args) => { //return Task.Run(() => { // //await Task.Delay(0); // var result = -(Math.Pow(200 - args[0].Current, 2) + Math.Pow(10 - args[1].Current, 2) + // Math.Pow(70 - args[2].Current, 2)); // return result; //}); var result = -(Math.Pow(20 - args[0].Current, 2) + Math.Pow(10 - args[1].Current, 2) + Math.Pow(70 - args[2].Current, 2)); return(new ValueTask <double>(result)); }; var maximizer = new GridOptimizer <double>(pars, targetFunc); EvalFolderFunc <double, EvalAddress> folder = FolderFunc; // NB Func<> cannot have byref params //Func<EvalAddress, GridOptimizer.EvalParametersResult, EvalAddress> folder = (state, item) => { // if (item.Value > state.Value) { // return new EvalAddress() { Value = item.Value, LinearAddress = item.Parameters.LinearAddress() }; // } // return state; //}; var optimum = await maximizer.FoldGrid(EvalAddress.Worst, folder); var optParams = pars.SetPositionsFromLinearAddress(optimum.LinearAddress); var endMemory = GC.GetTotalMemory(false); sw.Stop(); Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds}"); Console.WriteLine($"Memory: {endMemory - startMemory}"); Console.WriteLine($"Optimum: {optParams[0].Current} - {optParams[1].Current} - {optParams[2].Current}"); }
/// <summary> /// Iterate over grid, evaluate target function and fold its results. /// </summary> public async Task <TState> FoldGrid <TState>(TState seed, EvalFolderFunc <TEval, TState> evalFolder, Parameters parameters = null) { parameters = parameters ?? _parameters.Clone().Reset(); var accumulator = seed; var depth = 0; while (true) { if (depth < _parameters.Count) { if (parameters.List[depth].MoveNext()) { depth++; // when all moved, the first `if` above is false } else { parameters.List[depth].Reset(); depth--; } } else { try { var position = _tail % _concurrencyLimit; if (_activeTasks.Count < _concurrencyLimit) { var point = parameters.Clone(); var address = point.LinearAddress(); TEval tmp; var t = (_memoize && _results.TryGetValue(address, out tmp)) ? new ValueTask <TEval>(tmp) : _targetFunc(point); _activeTasks.Add(new EvalResultTask { Parameters = point, Value = t }); } else { // now the active tasks buffer is full // await on the task at the position, process the result and replace the task at the position // if current task is slow and other tasks already completed, we will just replace them very quickly // later, but while we are waiting for the tail task concurrency could drop, // so here is 'amortized' concurrency. var currentTask = _activeTasks[position]; await currentTask.Value; var evalResult = new EvalParametersResult <TEval>() { Value = currentTask.Value.Result, Parameters = currentTask.Parameters }; var address = currentTask.Parameters.LinearAddress(); TEval tmp; if (_memoize) { if (!_results.TryGetValue(address, out tmp)) { _results[address] = currentTask.Value.Result; } } // NB this is single-threaded application of evalFolder and evalResult.Parameters could be modified // later since they are reused. Folder should copy parameters or store linear address accumulator = evalFolder(accumulator, ref evalResult); for (int i = 0; i < parameters.Count; i++) { currentTask.Parameters.List[i] = parameters.List[i]; } address = currentTask.Parameters.LinearAddress(); var t = (_memoize && _results.TryGetValue(address, out tmp)) ? new ValueTask <TEval>(tmp) : _targetFunc(currentTask.Parameters); currentTask.Value = t; _activeTasks[position] = currentTask; _tail++; } } finally { depth--; } } if (depth == -1) { break; } } // NB All items in _activeTask are active foreach (var currentTask in _activeTasks) { await currentTask.Value; var evalResult = new EvalParametersResult <TEval> { Value = currentTask.Value.Result, Parameters = currentTask.Parameters }; accumulator = evalFolder(accumulator, ref evalResult); } return(accumulator); }