Осуществующий вопрос: Лучшая производительность?
-
19-09-2019 - |
Вопрос
Быстрый вопрос:
Какой из них быстрее?
foreach (Object obj in Collection)
{
if(obj.Mandatory){ ... }
}
или же
foreach (Object obj in Collection.FindAll(o => o.Mandatory))
{
...
}
И если вы знаете более быстрое предложение, я был бы рад узнать.
Спасибо
Решение
Следующий тестовый код печатает систему тиков (1 тик = 100 наносекунд) для итерации через 10 миллионов объектов. Findall самый медленный, а цикл быстрее, как и ожидалось.
Но накладные расходы итерации измеряются в наносекунд по предмету даже в худшем случае. Если вы делаете что -то значимое в цикле (например, что -то, что занимает микросекунду на предмет), то разница в скорости итерации - это совершенно незначительный.
Так что ради любви к Тьюрингу не запрещайте Foreach в ваших рекомендациях по кодированию сейчас. Это не имеет никакого практического значения, и операторы LINQ, безусловно, легче читать.
public class Test
{
public bool Bool { get; set; }
}
class Program
{
static void Main(string[] args)
{
// fill test list
var list = new List<Test>();
for (int i=0; i<1e7; i++)
{
list.Add(new Test() { Bool = (i % 2 == 0) });
}
// warm-up
int counter = 0;
DateTime start = DateTime.Now;
for (int i = 0; i < list.Count; i++)
{
if (list[i].Bool)
{
counter++;
}
}
// List.FindAll
counter = 0;
start = DateTime.Now;
foreach (var test in list.FindAll(x => x.Bool))
{
counter++;
}
Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 7969158
// IEnumerable.Where
counter = 0;
start = DateTime.Now;
foreach (var test in list.Where(x => x.Bool))
{
counter++;
}
Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 5156514
// for loop
counter = 0;
start = DateTime.Now;
for (int i = 0; i < list.Count; i++)
{
if (list[i].Bool)
{
counter++;
}
}
Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 2968902
}
Другие советы
Если твой Collection
это List<T>
тогда FindAll
реализуется путем создания нового List<T>
и копирование всех элементов, которые соответствуют предикату. Это, очевидно, медленнее, чем просто перечисление коллекции и принятие решения для каждого элемента, если предикат содержится.
Если вы используете .NET 3.5, вы можете использовать LINQ, который не будет создавать копию и похож на ваш первый пример:
foreach (object obj in someCollection.Where(o => o.Mandatory))
{
...
}
Обратите внимание, что это не обязательно самое быстрое решение. Просто легко увидеть этот метод, который выделяет память а также перечисляет коллекцию медленнее, чем метод, который Только перечисляет коллекцию. Если производительность имеет решающее значение: измерьте это.
Первый будет несколько быстрее.
Во втором случае вы используете List<T>.FindAll
Чтобы создать временный список, который соответствует вашим критериям. Это копирует список, затем итерация над ним.
Тем не менее, вы можете сделать то же самое с той же скоростью, что и ваш первый вариант, делая:
foreach (Object obj in Collection.Where(o => o.Mandatory))
{
}
Это потому что Перечисляется. Где использует потоковую передачу, чтобы вернуть IEnumerable<T>
, который генерируется по мере использования. Копия не сделана.
Самый быстрый вы когда -либо могли получить, не параллелизируя перечисление в несколько потоков, принимая учетные записи о количестве процессоров и т. Д.
for (int i = 0; i < Collection.Count; i++)
{
var item = Collection[i];
if (item.Mandatory) { ... }
}
Я бы порекомендовал вас, хотя всегда использовать LINQ, а не писать for
или же foreach
Петли, потому что в будущем он станет настолько умным, что на самом деле будет способно распространять работу по процессорам и учитывать аппаратные вещи (см. Plinq), и в конечном итоге она будет быстрее, чем если бы вы написали петли самостоятельно: декларатив против императива программирование.
Findall - это просто синтаксический сахар. Например:
List<string> myStrings = new List<string>();
foreach (string str in myStrings.FindAll(o => o.Length > 0))
{
}
Компилируется по:
List<string> list = new List<string>();
if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
CS$<>9__CachedAnonymousMethodDelegate1 = new Predicate<string>(MyClass.<RunSnippet>b__0);
}
using (List<string>.Enumerator enumerator = list.FindAll(CS$<>9__CachedAnonymousMethodDelegate1).GetEnumerator())
{
while (enumerator.MoveNext())
{
string current = enumerator.Current;
}
}
public List<T> FindAll(Predicate<T> match)
{
if (match == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
List<T> list = new List<T>();
for (int i = 0; i < this._size; i++)
{
if (match(this._items[i]))
{
list.Add(this._items[i]);
}
}
return list;
}
private static bool <RunSnippet>b__0(string o)
{
return (o.Length > 0);
}
Если производительность под вопросом, это, вероятно, не является узким местом, однако, рассматривали ли вы использование параллельной библиотеки или plinq? Смотри ниже:
Parallel.ForEach(Collection, obj =>
{
if (obj.Mandatory)
{
DoWork();
}
});
http://msdn.microsoft.com/en-us/library/dd460688(v=vs.110).aspx
Кроме того, хотя, возможно, немного не связанный с этим, кажется, что производительность заглядывает в ваше любопытство, если вы имеете дело с очень большими наборами данных, может быть полезен бинарный поиск. В моем случае у меня есть два отдельных списка данных. Мне приходится иметь дело со списками миллионов записей, и это спасло меня буквально экспоненциальное количество времени на исполнение. Единственным недостатком является то, что он полезен только для очень больших коллекций и должен быть отсортирован заранее. Вы также заметите, что это использует класс ConcurrentDictionary, который обеспечивает значительные накладные расходы, но он безопасен и требовался из -за требований и количества потоков, которыми я управляю асинхронно.
private ConcurrentDictionary<string, string> items;
private List<string> HashedListSource { get; set; }
private List<string> HashedListTarget { get; set; }
this.HashedListTarget.Sort();
this.items.OrderBy(x => x.Value);
private void SetDifferences()
{
for (int i = 0; i < this.HashedListSource.Count; i++)
{
if (this.HashedListTarget.BinarySearch(this.HashedListSource[i]) < 0)
{
this.Mismatch.Add(items.ElementAt(i).Key);
}
}
}
Это изображение было первоначально размещено в отличной статье, найденной здесь: http://letsalgorithm.blogspot.com/2012/02/intersing-two-sorted-integer-arrays.html
Надеюсь это поможет!