2009-12-13 2 views
2

У меня есть этот код:C# * Странные * проблемы с секундомером и петлей Еогеаспа

var options = GetOptions(From, Value, SelectedValue);  
var stopWatch = System.Diagnostics.Stopwatch.StartNew(); 

foreach (Option option in options) 
{ 
    stringBuilder.Append("<option"); 

    stringBuilder.Append(" value=\""); 
    stringBuilder.Append(option.Value); 
    stringBuilder.Append("\""); 

    if (option.Selected) 
     stringBuilder.Append(" selected=\"selected\""); 

    stringBuilder.Append('>'); 

    stringBuilder.Append(option.Text); 
    stringBuilder.Append("</option>"); 
} 

HttpContext.Current.Response.Write("<b>" + stopWatch.Elapsed.ToString() + "</b><br>"); 

Он пишет:
00: 00: 00.0004255 в первой попытке (не в отладке)
00 : 00: 00.0004260 во второй попытке и
00: 00: 00.0004281 с третьей попытки.

Теперь, если я изменить код, чтобы эта мера будет внутри цикла Еогеасп:

var options = GetOptions(From, Value, SelectedValue);  

foreach (Option option in options) 
{ 
    var stopWatch = System.Diagnostics.Stopwatch.StartNew(); 

    stringBuilder.Append("<option"); 

    stringBuilder.Append(" value=\""); 
    stringBuilder.Append(option.Value); 
    stringBuilder.Append("\""); 

    if (option.Selected) 
     stringBuilder.Append(" selected=\"selected\""); 

    stringBuilder.Append('>'); 

    stringBuilder.Append(option.Text); 
    stringBuilder.Append("</option>"); 

    HttpContext.Current.Response.Write("<b>" + stopWatch.Elapsed.ToString() + "</b><br>"); 
} 

... Я получаю
[00: 00: 00,0000014, 00: 00: 00,0000011] = 00: 00: 00.0000025 с первой попытки (не отлаживается),
[00: 00: 00.0000016, 00: 00: 00.0000011] = 00: 00: 00.0000011] = 00: 00: 00.0000027 во второй попытке и
[00: 00: 00.0000013] , 00: 00: 00.0000011] = 00: 00: 00.0000024 с третьей попытки.

?!
Это абсолютно неудовлетворительно в соответствии с первыми результатами ... Я слышал, что петля медленная, но никогда не предполагала, что это , поэтому медленный ... Это что?

options имеет 2 опции. Вот option класс, если это необходимо:

public class Option 
{ 
    public Option(string text, string value, bool selected) 
    { 
     Text = text; 
     Value = value; 
     Selected = selected; 
    } 

    public string Text 
    { 
     get; 
     set; 
    } 

    public string Value 
    { 
     get; 
     set; 
    } 

    public bool Selected 
    { 
     get; 
     set; 
    } 
} 

Спасибо.

+0

Ну, на самом деле, в чем вопрос? «Почему случай 2 часа быстрее, чем случай 1?» Или это «почему цикл« foreach »настолько медленный?» –

+0

Почему случай 2 часа быстрее, чем случай 1 –

+0

@Alon: Я вижу, поэтому вы спрашиваете, почему итерация через цикл foreach (исключая операции с циклом) настолько медленная? Да? –

ответ

7

Цикл foreach сам по себе не имеет никакого отношения к разнице во времени.

Что возвращает метод GetOptions? Я предполагаю, что он не возвращает набор параметров, а скорее перечислитель, способный получить параметры. Это означает, что на самом деле выбор параметров не выполняется, пока вы не начнете их повторять.

В первом случае вы запускаете часы перед запуском итерации параметров, что означает, что время для выбора параметров включено во время.

Во втором случае вы начинаете часы после начиная переборе вариантов, что означает, что время для выборки параметров является не включены во время.

Итак, разница во времени, которую вы видите не из-за самого цикла foreach, это время, необходимое для выбора параметров.

Вы можете убедиться, что параметры выбираются сразу, прочитав их в коллекцию:

var options = GetOptions(From, Value, SelectedValue).ToList(); 

Теперь измерить производительность, и вы увидите очень мало разницы.

0

Первый пример кода ничего не выводит до тех пор, пока все параметры не будут повторены, а вторая выведет время после обработки первой опции. Если есть несколько вариантов, вы ожидаете увидеть такую ​​разницу.

+0

Во втором примере кода я написал, сколько времени каждый параметр принимает в каждой попытке, и это далеко от первого примера кода. –

+0

Какой тип вариантов? foreach создаст перечислитель, который нужно будет утилизировать. Утилизация может сжечь некоторые циклы процессора. – recursive

+0

- это IEnumerable

1

Если вы измеряете время, затраченное на выполнение 160 раз, оно обычно занимает порядка 160 раз дольше, чем измерение времени, необходимого для его выполнения. Вы предполагаете, что содержимое цикла выполняется только один раз, или вы пытаетесь сравнить мел и сыр?

В первом случае попробуйте изменить последнюю строку кода с помощью stopWatch.Elapsed.ToString() к stopWatch.Elapsed.ToString()/options.Count

Это будет по крайней мере, среднее вы сравниваете одну итерацию с одной итерацией.

Однако ваши результаты все равно будут бесполезны. Сроки очень короткой операции дают плохие результаты - вы должны повторять такую ​​вещь десятки тысяч раз, чтобы получить статистически значимое среднее время. В противном случае погрешность системных часов и накладных расходов, связанных с запуском и остановкой таймера, приведет к болоту ваших результатов.

Также, что делает ПК, когда все это происходит? Если есть другие процессы, загружающие CPU, они могут легко помешать вашим таймингам. Если вы запускаете это на занятом сервере, вы можете получить результаты со случайными результатами.

Наконец, как вы проводите тесты, вы можете изменить ситуацию. Если вы всегда запускаете тест 1, за которым следует тест 2, возможно, что запуск первого теста влияет на кеши процессора (например,данных в списке опций) и т. д., чтобы следующий код мог выполнять быстрее. Если сбор мусора происходит во время одного из ваших тестов, это приведет к искажению результатов.

Вам необходимо устранить все эти факторы, прежде чем у вас есть номера, которые стоит сравнить. Только тогда вы должны спросить: «Почему тест 1 работает так медленнее, чем тест 2»?

0

Всего pause it a few times in the IDE, и вы увидите, куда идет время.

Существует очень естественный и сильный соблазн подумать, что время, которое требуется, пропорционально количеству кода. Например, как вы думаете, быстрее?

for (MyClass x in y) 

for (MyClass theParticularInstanceOfClass in MyCollectionOfInstances) 

Естественно думать, что первый быстрее, когда на самом деле размер кода не имеет значения и может скрываться множество дорогостоящих операций.