Тестирование производительности функций для обертывания индекс


После задавая этот вопрос, я решила написать тест и определить самый быстрый способ обернуть индекс (где мой параметр maxsize - это всегда сила 2).

Есть 3 функции, которые я сравниваю:

// plain wrap
public static int WrapIndex(int index, int endIndex, int maxSize)
{
    return (endIndex + index) > maxSize ? (endIndex + index) - maxSize : endIndex + index;
}

// wrap using mod
public static int WrapIndexMod(int index, int endIndex, int maxSize)
{
    return (endIndex + index) % maxSize;
}

// wrap my masking out the top bits
public static int WrapIndexMask(int index, int endIndex, int maxSize)
{
    return (endIndex + index) & (maxSize - 1);
}

Вот мой тест:

public static void WrapTest(int numRuns = 10000)
{
    int index = 256;
    int endIndex = 0;
    int maxSize = 4096;

    long wrapPlain = 0;
    long wrapMod = 0;
    long wrapMask = 0;

    Stopwatch sw = new Stopwatch();

    for (int i = 0; i < numRuns; i++)
    {

        // plain
        sw.Start();
        for (int j = 0; j < numRuns; j++)
        {
            WrapIndex(index, endIndex, maxSize);
        }
        sw.Stop();
        wrapPlain += sw.ElapsedTicks;
        sw.Reset();

        // mod
        sw.Start();
        for (int j = 0; j < numRuns; j++)
        {
            WrapIndexMod(index, endIndex, maxSize);
        }
        sw.Stop();
        wrapMod += sw.ElapsedTicks;
        sw.Reset();

        // mask
        sw.Start();
        for (int j = 0; j < numRuns; j++)
        {
            WrapIndexMask(index, endIndex, maxSize);
        }
        sw.Stop();
        wrapMask += sw.ElapsedTicks;
        sw.Reset();

        // change indexes
        endIndex++;
        endIndex = endIndex % maxSize;

        index++;
        index = index % maxSize;
    }
    Console.WriteLine(String.Format("Plain: {0} Mod: {1} Mask: {2}", wrapPlain / numRuns, wrapMod / numRuns, wrapMask / numRuns));
}

Я прошел тест и я постоянно получаю следующие результаты (в ТИКах):

Plain: 25 Mod: 16 Mask: 16 (maxSize = 512)
Plain: 25 Mod: 17 Mask: 17 (maxSize = 1024)
Plain: 25 Mod: 17 Mask: 17 (maxSize = 4096)

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

Мой тест действителен? Есть ли лучший способ для тестирования производительности?



2729
6
задан 5 февраля 2011 в 01:02 Источник Поделиться
Комментарии
1 ответ

Ваши результаты не показаться странным, так что я попытался переписать свой тест и посмотреть, если я могу получить разные результаты. Я подозревал, что может быть запуск и остановка секундомера слишком часто, что может исказить результаты из-за потери точности при каждом запуске и остановке. В мой код, я только запускать и останавливать секундомер один раз для каждого типа расчета.

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

Вот типичный выход из одного из моих тестов:

Plain: 59.7033
Mod: 64.6872
Mask: 58.1923

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

И вот код:

static void Main(string[] args)
{
TestPerf(WrapIndex, "Plain");
TestPerf(WrapIndexMod, "Mod");
TestPerf(WrapIndexMask, "Mask");
}

public static void TestPerf(Func<int, int, int, int> getIndex, string label, int numRuns = 10000)
{
int maxSize = 4096;

Stopwatch sw = new Stopwatch();
sw.Start();

for (int i = 0; i < numRuns; i++)
{
for (int index = 0; index < maxSize; index++)
{
getIndex(index, 1234, maxSize);
}
}

sw.Stop();
Console.WriteLine(string.Format("{0}: {1}", label, ((double)sw.ElapsedTicks) / numRuns));
}

Редактировать: Также обратите внимание, что я бросил ElapsedTicks дважды перед делением, чтобы избежать округления. Вы должны быть осторожны, чтобы избежать округления в расчетах, потому что просто добавляет шум к конечному результату.

Правка 2:

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

Дело в том, что без этой обертки функций, джиттер может сделать некоторые серьезные оптимизации. Я знаю, что это джиттер, а не компилятор C#, потому что весь код является неизменным в выходной ildasm. На самом деле, на 2 из ваших 3 методов (моделирование и маску), он пришел к выводу, что методы не добившись ничего на всех (потому что они делают простой расчет, а возвращаемое значение-это просто отбрасываются), так что даже не беспокоить их.

Чтобы проверить это утверждение, просто закомментировать строку в WrapIndex() и вернуть обычный 0, который уверен, чтобы получить оптимизированные. Снова запустите программу, и вы увидите, что все три метода теперь одно и то же время. Нет никакого способа, что делает мод или маска имеет такую же стоимость как вернуть константу 0, так что говорит вам, что код просто не выполняется.

Это другой вопрос, вы должны знать, когда делать тесты производительности. Если ваш тест слишком простой, оптимизатор будет просто выбросить весь код.

В мой тест, функции оболочки, предотвращая дрожание от оптимизации, потому что он не знает, какой код будет выполняться, поэтому он может не inline, а затем выбросьте код, который является, почему вы получаете более разумные цифры.

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

4
ответ дан 5 февраля 2011 в 07:02 Источник Поделиться