Skip to content

Instantly share code, notes, and snippets.

@ReubenBond
Created March 24, 2025 18:12
Show Gist options
  • Save ReubenBond/b1ee13dffdbcbf32dd89876ebeaea6af to your computer and use it in GitHub Desktop.
Save ReubenBond/b1ee13dffdbcbf32dd89876ebeaea6af to your computer and use it in GitHub Desktop.
CallChainSimulation.cs
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