Рефакторинг доходности IEnumerableC#

Место общения программистов C#
Anonymous
Рефакторинг доходности IEnumerable

Сообщение Anonymous »

Сегодня я узнал кое-что новое.
У меня есть следующий код, который используется для создания простых графов, содержащих ссылки между узлами

Код: Выделить всё

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;
}
}
}
Поскольку существует большое количество узлов и ссылок, а также из-за того, что граф очень динамичен, я решил не хранить родительских и дочерних узлов каждого узла в самом экземпляре узла ( узел может иметь много родителей и много детей). Но вместо этого в классе Graph есть вспомогательные методы GetParents и GetChildren.
Однако я узнал кое-что новое. При этом для каждого вызова любого из этих методов выделяется вспомогательный код, необходимый для 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

Вернуться в «C#»