Оценка общего времени выполнения функции C путем измерения одной итерации

#c #performance #time #runtime #cumulative-sum

#c #Производительность #время #время выполнения #кумулятивная сумма

Вопрос:

Я реализовал метод c , который вычисляет максимальную ошибку ulp между аппроксимацией и эталонной функцией на заданном интервале. Приближение, а также ссылка вычисляются как значения с плавающей запятой одинарной точности. Метод начинается с нижней границы интервала и повторяется по каждому существующему значению с одинарной точностью в пределах диапазона.

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

Я попытался выполнить сравнение несколько раз, чтобы вычислить время выполнения одной итерации. Мой подход заключался в том, чтобы умножить продолжительность одной итерации на общее количество плавающих значений, существующих в диапазоне. Но, очевидно, время выполнения для одной итерации не является постоянным, а зависит от количества итераций, поэтому моя предполагаемая продолжительность вообще не точна… Может быть, можно было бы адаптировать вычисление общего времени выполнения в основном цикле?

Мой вопрос: есть ли какой-либо другой способ оценить общее время выполнения для этого конкретного случая?

Вот мой код:

 void FloatEvaluateMaxUlp(float(*testFunction)(float), float(*referenceFunction)(float), float lowBound, float highBound)
{
    /*initialization*/
    float x = lowBound, output, output_ref;
    int ulp = 0;
    long long duration = 0, numberOfFloats=0;

    /*calculate number of floats between lowBound and highBound*/
    numberOfFloats = *(int*)amp;highBound - *(int*)amp;lowBound;

    /*measure execution time of 10 iterations*/
    int iterationsToEstimateTime = 1000;
    auto t1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterationsToEstimateTime; i  )
    {
        printProgressInteger(i 1, iterationsToEstimateTime);
        output = testFunction(x);
        output_ref = referenceFunction(x);
        int ulp_local = FloatCompareULP(output, output_ref);
        if (abs(ulp_local) > abs(ulp))
            ulp = ulp_local;
        x= std::nextafter(x, highBound   0.001f);
    }
    auto t2 = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
    duration /= iterationsToEstimateTime;
    x = lowBound;
    


    /*output of estimated time*/
    std::cout <<std::endl<<std::endl<< " Number of floats: " << numberOfFloats << "   Time per iteration: " << duration << "  Estimated total time: " << numberOfFloats * duration << std::endl;
    std::cout << " Starting test in range [" << lowBound << "," << highBound << "]." << std::endl;


    long long count = 0;

    /*record start time*/
    t1 = std::chrono::high_resolution_clock::now();

    
    for (count; x < highBound; count  )
    {
        printProgressInteger(count, numberOfFloats);
        output = testFunction(x);
        output_ref = referenceFunction(x);
        int ulp_local = FloatCompareULP(output, output_ref);
        if (abs(ulp_local) > abs(ulp))
            ulp = ulp_local;
        x = std::nextafter(x, highBound   0.001f);

    }

    /*record stop time and compute duration*/
    t2 = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();

    /*result output*/
    std::cout <<std::endl<< std::endl << std::endl << std::endl << "*********************************************************" << std::endl;
    std::cout << "                   RESULT                                " << std::endl;
    std::cout << "*********************************************************" << std::endl;
    std::cout << " Iterations: " << count << "  Total execution time: " << duration << std::endl;
    std::cout << " Max ulp: " << ulp <<std::endl;
    std::cout << "*********************************************************" << std::endl;


}
  

Комментарии:

1. Для получения более точной оценки вам потребуется больше данных. Некоторые из ваших проблем или препятствий будут связаны с переключением задач операционной системой. Другим может быть разрешение ваших часов или измерительного устройства. Помните, что современные процессоры выполняют инструкции в диапазоне наносекунд. Если вы выполните больше итераций, вы можете преобразовать свои измерения во что-то более надежное, например, в миллисекунды или секунды. К сожалению, часы не справляются с этой скоростью. Другой вопрос заключается в разрешении часов операционной системы, часы H / W могут иметь более высокое разрешение, но часы операционной системы могут и не иметь.

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

3. Поскольку у вас, вероятно, нет измерительных приборов или контрольных точек, вам нужно будет выполнить не менее 1E06 итераций между измерениями часов, чтобы получить значимое среднее значение. Опять же, вы хотите устранить выбросы и шумы в операционной системе. В статистике одной выборки никогда не бывает достаточно.

4. Спасибо за ваше объяснение! Вы правы, у меня нет ни измерительных приборов, ни контрольных точек, это невозможно. Итак, я прав, предполагая, что вы бы порекомендовали установить iterationsToEstimateTime равным 1e6 для устранения шума и т.д.?

5. Как говорится в статистике, чем больше данных, тем лучше среднее качество.