У меня есть следующий код, который используется для создания простых графов, содержащих ссылки между узлами
Код: Выделить всё
public class Node(string name)
{
public string Name { get; } = name;
}
public class Link(Node from, Node to)
{
public Node From { get; } = from;
public Node To { get; } = to;
}
public class Graph
{
public List Nodes = new();
public List Links = new();
public IEnumerable GetParents(Node node)
{
foreach (var link in Links)
{
if (link.To == node)
yield return link.From;
}
}
public IEnumerable GetChildren(Node node)
{
foreach (var link in Links)
{
if (link.From == node)
yield return link.To;
}
}
}
Однако я узнал кое-что новое. При этом для каждого вызова любого из этих методов выделяется вспомогательный код, необходимый для IEnumerable/yield.
Как показано в следующем тесте:
Код: Выделить всё
[MemoryDiagnoser]
public class Test
{
Graph _graph;
[IterationSetup]
public void Setup()
{
_graph = new Graph();
Node a = new("A"), b = new("B"), c = new("C");
_graph.Nodes = [a, b, c];
_graph.Links = [new Link(a, b), new Link(a, c)];
}
[Benchmark]
public void Run1()
{
_graph.GetParents(_graph.Nodes[1]);
}
[Benchmark]
public void Run2()
{
_graph.GetParents(_graph.Nodes[1]);
_graph.GetParents(_graph.Nodes[1]);
}
[Benchmark]
public void RunLoop()
{
for (int i = 0; i < 1000; ++i)
_graph.GetParents(_graph.Nodes[1]);
}
[Benchmark]
public void GetChildren_Raw()
{
int count = 0;
for (int i = 0; i < 1000; ++i)
{
foreach (var link in _graph.Links)
{
if (link.From == _graph.Nodes[1])
{
count++;
}
}
}
}
}
Код: Выделить всё
| Method | Mean | Error | StdDev | Median | Allocated |
|---------------- |------------:|------------:|-------------:|------------:|----------:|
| Run1 | 673.6 ns | 24.84 ns | 69.66 ns | 700.0 ns | 480 B |
| Run2 | 740.0 ns | 28.49 ns | 81.74 ns | 700.0 ns | 560 B |
| RunLoop | 31,390.9 ns | 4,295.45 ns | 12,597.80 ns | 35,600.0 ns | 80400 B |
| GetChildren_Raw | 14,674.2 ns | 455.90 ns | 1,322.66 ns | 14,100.0 ns | 400 B |
Подробнее здесь: https://stackoverflow.com/questions/790 ... blet-yield