Код предлагает, и ваш анализ в комментариях подтвердил, что величина аргумента sinf()
может быть довольно большой, конечно, до нескольких тысяч. Точное уменьшение аргумента, используемое в общих библиотечных реализациях триггерных функций, может быть интенсивным с вычислительной точки зрения и, таким образом, медленным для больших аргументов, особенно когда аппаратная платформа не поддерживает операции с добавлением расширенного умножения. Это, скорее всего, является фактором, влияющим на низкий уровень производительности sinf()
.
Вы упомянули в комментарии, что операнды до sinf()
включают множитель π. Это означает, что вы действительно хотите использовать sinpif()
, где sinpi (x) = sin (x * π). Функция sinpi была введена в стандарте IEEE-754 (2008) с плавающей запятой, но пока не дошла до языковых стандартов. Однако несколько цепей инструментов предлагают его как расширение. Преимущество sinpi()
состоит в том, что он требует только очень простого сокращения аргументов независимо от величины аргументов, что может значительно сократить время извлечения. Это приводит к повышению производительности. Так как умножение на π неявно, оно также может обеспечить улучшенную точность по дискретному подходу с использованием sinf()
.
Я показываю примерную реализацию C99 sinpif()
ниже. Обратите внимание, что этот код в значительной степени зависит от стандартной математической функции fmaf()
для достижения высокой скорости обработки и отличной точности. Если ваш процессор не имеет аппаратной поддержки для плавной операции многократного добавления (FMA), эта функция будет выполняться очень медленно, так как правильная эмуляция fmaf()
нетривиальна. Так как код написан модульным способом, вы хотите настроить компилятор для применения максимального количества функций вложения или добавить соответствующие атрибуты вложения для всех составных функций.
Как вы указали, что ваша аппаратная платформа не предлагает встроенную поддержку FMA, вы можете заменить каждый fmaf(a,b,c)
на (a*b+c)
с некоторой потерей точности. Согласно моим тестам, максимальная ошибка ulp увеличивается до 1,71364 ulps. Это все еще очень хорошо, но my_sinf()
больше не будет точно округлено больше в этом случае, что обычно считается желательным свойством.
/* Argument reduction for sinpi, cospi, sincospi. Reduces to [-0.25, +0.25] */
float trig_red_pi_f (float a, int *i)
{
float r;
r = rintf (a + a);
*i = (int)r;
r = a - 0.5f * r;
return r;
}
/* Approximate cos(pi*x) for x in [-0.25,0.25]. Maximum ulp error = 0.87440 */
float cospif_poly (float s)
{
float r;
r = 0x1.d98dcep-3f; // 2.31227502e-1f
r = fmaf (r, s, -0x1.55c4e8p+0f); // -1.33503580e+0f
r = fmaf (r, s, 0x1.03c1d4p+2f); // 4.05870533e+0f
r = fmaf (r, s, -0x1.3bd3ccp+2f); // -4.93480206e+0f
r = fmaf (r, s, 0x1.000000p+0f); // 1.00000000e+0f
return r;
}
/* Approximate sin(pi*x) for x in [-0.25,0.25]. Maximum ulp error = 0.96441 */
float sinpif_poly (float a, float s)
{
float r;
r = -0x1.2dc6f8p-1f; // -5.89408636e-1f
r = fmaf (r, s, 0x1.46602ep+1f); // 2.54981017e+0f
r = fmaf (r, s, -0x1.4abbc0p+2f); // -5.16770935e+0f
r = r * s;
r = fmaf (r, a, -0x1.777a5cp-24f * a); // PI_lo // -8.74227766e-8f
r = fmaf (a, 0x1.921fb6p+1f, r); // PI_hi // 3.14159274e+0f
return r;
}
/* Compute sin(pi*x) and cos(pi*x) based on quadrant */
float sinpif_cospif_core (float a, int i)
{
float r, s;
s = a * a;
r = (i & 1) ? cospif_poly (s) : sinpif_poly (a, s);
if (i & 2) {
r = 0.0f - r; // don't change "sign" of NaNs or create negative zeros
}
return r;
}
/* maximum ulp error = 0.96411 */
float my_sinpif (float a)
{
float r;
int i;
r = trig_red_pi_f (a, &i);
r = sinpif_cospif_core (r, i);
/* IEEE-754: sinPi(+n) is +0 and sinPi(-n) is -0 for positive integers n */
r = (a == truncf (a)) ? (a * 0.0f) : r;
return r;
}
Разве это не тот процессор, который намного медленнее, чем другой? Каким другим процессором вы все равно сравниваете это, вероятно, с некоторыми i5 или i7? Очевидно, собирается выпустить средний A9 прямо из воды. – harold
Является ли ваша проблема составлением кода или ** работает **? –
Проблема во время выполнения, но сравнение между двумя процессорами в моем случае не очень важно. Программа огромна, содержит много звуковых алгоритмов в реальном времени, и если я исключаю только те части, которые используют функции sin(), они работают почти с одинаковой скоростью. Я работаю над эталонной ссылкой: я знаю, что если someting принимает 100 мс для запуска на моем i7, для работы на A9 требуется ~ 900 мс, поэтому перед компиляцией чего-либо я запускаю свой тест, чтобы я знал, как он будет работать на A9 перед запуском. –