Created
March 24, 2025 18:12
-
-
Save ReubenBond/b1ee13dffdbcbf32dd89876ebeaea6af to your computer and use it in GitHub Desktop.
CallChainSimulation.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Diagnostics; | |
using System.Text; | |
int messageCount = 100; | |
var results = new List<SimulationResults>(); | |
int[] hostCounts = [1, 2, 3, 6]; | |
foreach (var n in hostCounts) | |
{ | |
var simulator = new DistributedSimulator(n, depth: 10); | |
var runResult = simulator.RunSimulation(messageCount); | |
runResult.HostCount = n; | |
results.Add(runResult); | |
} | |
if (results.Count > 0) | |
{ | |
var baseline = results.First(); | |
foreach (var result in results) | |
{ | |
result.RelativeLatency = result.AverageLatency / baseline.AverageLatency; | |
result.RelativeThroughput = result.Throughput / baseline.Throughput; | |
} | |
} | |
Console.WriteLine("\nResults:"); | |
Console.WriteLine(GenerateMarkdownTable(results)); | |
static string GenerateMarkdownTable(List<SimulationResults> results) | |
{ | |
var sb = new StringBuilder(); | |
// Table header | |
sb.AppendLine("| N (Host Count) | Relative Latency | Relative Throughput |"); | |
sb.AppendLine("|---------------|-----------------|-------------------|"); | |
// Table rows | |
foreach (var result in results) | |
{ | |
sb.AppendLine($"| {result.HostCount} | {result.RelativeLatency:F2}x | {result.RelativeThroughput:F2}x |"); | |
} | |
return sb.ToString(); | |
} | |
sealed class DistributedSimulator(int hostCount, int depth) | |
{ | |
private static readonly bool LocalChildren = true; | |
private static readonly bool StatelessChildren = false; | |
private const int RpcCost = 25; | |
private readonly Random _random = new(); | |
public SimulationResults RunSimulation(int messageCount) | |
{ | |
var results = new SimulationResults(); | |
for (int i = 0; i < messageCount; i++) | |
{ | |
// Select random hosts G, S, D | |
int g = _random.Next(hostCount); | |
int s = _random.Next(hostCount); | |
int d = _random.Next(hostCount); | |
// Calculate latency for this message path | |
int latency = CalculateClientToGrainCallLatency(g, s, d); | |
if (StatelessChildren) | |
{ | |
latency += depth - 1; | |
} | |
else if (LocalChildren) | |
{ | |
var childrenDirectories = Enumerable.Range(1, depth - 1).Select(_ => _random.Next(hostCount)).ToArray(); | |
latency += CalculateChildLatency(s, childrenDirectories); | |
} | |
else | |
{ | |
// Randomly placed children. | |
var childrenSilos = Enumerable.Range(1, depth - 1).Select(_ => _random.Next(hostCount)).ToArray(); | |
var childrenDirectories = Enumerable.Range(1, depth - 1).Select(_ => _random.Next(hostCount)).ToArray(); | |
latency += CalculateChildLatency(s, childrenSilos, childrenDirectories); | |
} | |
results.TotalLatency += latency; | |
} | |
// Calculate results | |
results.AverageLatency = (double)results.TotalLatency / messageCount; | |
results.Throughput = 1000 / results.AverageLatency; | |
return results; | |
} | |
private static int CalculateChildLatency(int s, int[] childrenDirectories) | |
{ | |
var latency = 0; | |
foreach (var d in childrenDirectories) | |
{ | |
if (s == d) | |
{ | |
latency += 1; | |
} | |
else | |
{ | |
latency += RpcCost; | |
} | |
} | |
return latency; | |
} | |
private static int CalculateChildLatency(int g, int[] childrenSilos, int[] childrenDirectories) | |
{ | |
var latency = 0; | |
Debug.Assert(childrenDirectories.Length == childrenSilos.Length); | |
for (var i = 0; i < childrenDirectories.Length; i++) | |
{ | |
var s = childrenSilos[i]; | |
var d = childrenDirectories[i]; | |
// G to S | |
latency += (g == s) ? 1 : RpcCost; | |
// S to D | |
latency += (s == d) ? 1 : RpcCost; | |
} | |
return latency; | |
} | |
private static int CalculateClientToGrainCallLatency(int g, int s, int d) | |
{ | |
int latency = 0; | |
// Client to G | |
latency += RpcCost; // Client is always a different host | |
// G to S | |
latency += (g == s) ? 1 : RpcCost; | |
// S to D | |
latency += (s == d) ? 1 : RpcCost; | |
return latency; | |
} | |
} | |
sealed class SimulationResults | |
{ | |
public int HostCount { get; set; } | |
public long TotalLatency { get; set; } = 0; | |
public double AverageLatency { get; set; } = 0; | |
public double Throughput { get; set; } = 0; | |
public double RelativeLatency { get; set; } = 1.0; // Relative to n=1 case | |
public double RelativeThroughput { get; set; } = 1.0; // Relative to n=1 case | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment