Я не понимаю, почему один IEnumerable.Contains () работает быстрее, чем другой в следующем фрагменте, даже если они идентичны.
public class Group
{
public static Dictionary<int, Group> groups = new Dictionary<int, Group>();
// Members, user and groups
public List<string> Users = new List<string>();
public List<int> GroupIds = new List<int>();
public IEnumerable<string> AggregateUsers()
{
IEnumerable<string> aggregatedUsers = Users.AsEnumerable();
foreach (int id in GroupIds)
aggregatedUsers = aggregatedUsers.Concat(groups[id].AggregateUsers());
return aggregatedUsers;
}
}
static void Main(string[] args)
{
for (int i = 0; i < 1000; i++)
Group.groups.TryAdd(i, new Group());
for (int i = 0; i < 999; i++)
Group.groups[i + 1].GroupIds.Add(i);
for (int i = 0; i < 10000; i++)
Group.groups[i/10].Users.Add($"user{i}");
IEnumerable<string> users = Group.groups[999].AggregateUsers();
Stopwatch stopwatch = Stopwatch.StartNew();
bool contains1 = users.Contains("user0");
Console.WriteLine($"Search through IEnumerable from recursive function was {contains1} and took {stopwatch.ElapsedMilliseconds} ms");
users = Enumerable.Empty<string>();
foreach (Group group in Group.groups.Values.Reverse())
users = users.Concat(group.Users);
stopwatch = Stopwatch.StartNew();
bool contains2 = users.Contains("user0");
Console.WriteLine($"Search through IEnumerable from foreach was {contains2} and took {stopwatch.ElapsedMilliseconds} ms");
Console.Read();
}
Вот вывод, полученный при выполнении этого фрагмента:
Search through IEnumerable from recursive function was True and took 40 ms
Search through IEnumerable from foreach was True and took 3 ms
Фрагмент моделирует 10 000 пользователей, распределенных по 1000 группам по 10 пользователей в каждой.
Каждая группа может иметь 2 типа участников, пользователей (строка) или другие группы (целое число, представляющее идентификатор этой группы).
Каждая группа имеет предыдущую группу в качестве участника. Таким образом, группа 0 имеет 10 пользователей, группа 1 имеет 10 пользователей и пользователей из группы 0, группа 2 имеет 10 пользователей и пользователей группы 1 ... и здесь начинается рекурсия.
Цель поиска состоит в том, чтобы определить, является ли пользователь "user0" (который находится ближе к концу списка) членом группы 999 (которая через групповое отношение содержит все 10000 пользователей).
Вопрос в том, почему поиск занимает всего 3 мс для поиска через IEnumerable, построенный с помощью foreach, и в 10 раз больше для того же IEnumerable, построенного с помощью рекурсивного метода?