Выполнение математических функций? - PullRequest
4 голосов
/ 06 октября 2009

Я работаю с графиком данных акселерометра и пытаюсь исправить гравитацию. Для этого я получаю вектор ускорения в сферических координатах, уменьшаю радиус на 1 г и конвертирую обратно в декартову. Этот метод вызывается по таймеру каждые 0,03 секунды:

//poll accleration
ThreeAxisAcceleration current = self.accelerationData;

//math to correct for gravity:
float radius = sqrt(pow(current.x, 2) + pow(current.y, 2) + pow(current.z, 2));
float theta = atan2(current.y, current.x);
float phi = acos(current.z/radius);

//NSLog(@"SPHERICAL--- RADIUS: %.2f -- THETA: %.2f -- PHI: %.2f", radius, theta, phi);

radius = radius - 1.0;

float newX = radius * cos(theta) * sin(phi);
float newY = radius * sin(theta) * sin(phi);
float newZ = radius * cos(phi);

current = (ThreeAxisAcceleration){newX, newY, newZ};

//end math
NSValue *arrayVal = [NSValue value:&current withObjCType:@encode(ThreeAxisAcceleration)];

if ([_dataHistoryBuffer count] > self.bounds.size.width) {
    [_dataHistoryBuffer removeObjectAtIndex:0];
}

[_dataHistoryBuffer addObject:arrayVal];

[self setNeedsDisplay];

Каким-то образом, добавление гравитационной коррекции постепенно ужасно замедляет мой код. Мне трудно поверить, что эта математика может замедлить работу программы, но без нее она все равно может проходить через весь мой метод отображения, который довольно длительный. Есть ли варианты, которые я могу рассмотреть здесь, чтобы избежать этого? Я что-то упустил или математика так медленно? Я могу закомментировать теги // math и // end math и все будет в порядке.

Спасибо за любую помощь.

P.S. В случае, если это может иметь значение, кого это может заинтересовать, я программирую на какао, и этот метод принадлежит подклассу CALayer, с -drawInContext: осуществлено.

Ответы [ 12 ]

8 голосов
/ 06 октября 2009

Вы на iPhone? Попробуйте использовать варианты этих функций с плавающей точкой: powf, sqrtf и т. Д.

Более подробная информация содержится в пункте № 4 ответа Кендалла Хельмштеттера Гелнера на на этот ТАК вопрос .

6 голосов
/ 06 октября 2009

Помимо того факта, что теоретически невозможно просто вычленить гравитацию Земли, первым шагом, который я бы сделал, было бы сравнение каждой операции, которую вы выполняете (умножение, деление, грех, atan2). и т. д.), а затем разработать способ обхода операций, выполнение которых занимает значительно больше времени (или избегать вычисления проблемных операций). Убедитесь, что в тестах производительности используются те же типы данных, что и в готовом продукте.

Это классический пример компромисса между временем и точностью. Обычно есть несколько алгоритмов для выполнения одного и того же вычисления, и у вас также есть LUT / интерполяция.

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

6 голосов
/ 06 октября 2009

Обычный способ сократить вектор будет выглядеть следующим образом:

float originalMagnitude = sqrtf(current.x * current.x, current.y * current.y, current.z* current.z);
float desiredMagnitude = originalMagnitude - 1.0f;
float scaleFactor = (originalMagnitude != 0) ? desiredMagnitude / originalMagnitude : 0.0f; // avoid divide-by-zero

current.x *= scaleFactor;
current.y *= scaleFactor;
current.z *= scaleFactor;

Тем не менее, нет, вызов нескольких триггерных функций 33 раза в секунду не должен сильно замедлять вас. С другой стороны, -[NSMutableArray removeObjectAtIndex:] потенциально может быть медленным для большого массива. Кольцевой буфер (с использованием NSMutableArray или массива структур C) будет более эффективным.

5 голосов
/ 06 октября 2009
  • Профиль, не спекулируйте. Не изменяйте ни черта, пока не узнаете, что изменить.

Предполагая, что вы получите профиль, который показывает, что вся математика действительно замедляет вас:

  • Не никогда пишите pow(someFloat,2). Компилятор должен быть в состоянии оптимизировать это для вас, но часто, на более новом оборудовании, эти оптимизации могут еще не быть на месте. Это всегда должно быть записано someFloat * someFloat. Функция pow () обычно является самой дорогой функцией в математической библиотеке. Простое умножение всегда будет по крайней мере столь же быстрым, как и при вызове pow (), и всегда будет по крайней мере настолько же точным (при условии, что арифметика соответствует стандарту IEEE-754). Кроме того, компилятору проще оптимизировать.
  • При работе с float s в C используйте суффиксные формы функции математической библиотеки. sinf быстрее, чем sin. sqrtf быстрее, чем sqrt. Помимо более быстрой работы самих функций вы избегаете ненужных преобразований в double.
  • Если вы видите замедление на процессоре ARMv6 (не 3GS или новом iPod Touch), убедитесь, что вы не компилируете в код большого пальца, когда выполняете много вычислений с плавающей запятой. Набор команд большого пальца (до thumb2) не может получить доступ к регистрам VFP, и, следовательно, для каждой операции с плавающей запятой требуется прокладка. Это может быть довольно дорого.
  • Если вы просто хотите уменьшить длину вектора ускорения на 1,0 (подсказка: это не делает то, что вы хотите), есть более эффективные алгоритмы для этого.
0 голосов
/ 28 октября 2009

Полагаю, поскольку вы используете автоматически высвобождаемую память (для NSValue) каждые 0,03 секунды, вы, вероятно, не даете пулу много времени для освобождения. Я могу ошибаться - профилирование - единственный способ сказать это.

Попробуйте вручную распределить и освободить NSValue и посмотрите, имеет ли это значение.

0 голосов
/ 28 октября 2009

Этот метод выяснит, в чем проблема. Чем хуже ваше замедление, тем быстрее он его найдет. Предположения - это то, что вы подозреваете, но не знаете, например, думаете, что математика - это проблема. Догадки обычно ошибочны, по крайней мере, для начала. Если вы правы, образцы покажут вам. Если вы ошибаетесь, они покажут вам, что правильно. Это никогда не пропускает.

0 голосов
/ 06 октября 2009

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

double radius = sqrt(current.x * current.x 
                     + current.y * current.y 
                     + current.z * current.z);
double newRadius = radius - 1.0;
double scale = newRadius/radius;
current.x *= scale;
current.y *= scale;
current.z *= scale;
0 голосов
/ 06 октября 2009

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

По умолчанию весь код для iPhone скомпилирован для набора инструкций Thumb-1. Thumb-1 не имеет встроенной поддержки с плавающей запятой, поэтому в конечном итоге он обращается к реализации ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ с плавающей запятой. Есть 2 способа справиться с этим:

  1. Скомпилируйте код для ARM. Процессор в iPhone может свободно смешивать Thumb и ARM-код, поэтому вы можете просто скомпилировать нужные фрагменты как ARM. Следует отметить, что GCC (и через прокси-код Xcode) не может скомпилировать отдельную функцию как ARM, вам нужно изолировать весь соответствующий код в его модуле компиляции. Вероятно, проще всего настроить весь проект на компиляцию для ARM, чтобы увидеть, исправляет ли он какие-либо проблемы (снимите флажок «Параметры сборки»> «Скомпилировать для большого пальца»). Вы должны заметить, что, хотя ARM ускоряет работу с плавающей запятой, это снижает плотность команд, тем самым снижая эффективность кэширования и снижая эффективность всего вашего другого кода, поэтому старайтесь избегать его, где только можете.
  2. Компиляция для Thumb-2. Thumb-2 - это расширенная версия Thumb, в которой добавлена ​​поддержка некоторых операций с плавающей запятой. Он доступен только на iPhone 3GS и новом iPod Touch, поэтому этот вариант не подходит для вас. Вы можете сделать это, переключив свою архитектуру на «Оптимизированный», что позволит создать толстый двоичный файл с текущей медленной версией для старых устройств и более быстрой версией для тех, которые поддерживают.

Вы также можете комбинировать оба эти варианта, если это кажется лучшим выбором.

0 голосов
/ 06 октября 2009

Просто из интереса - знаете ли вы, как реализована функция Math SQRT? Если он использует неэффективный алгоритм приближения, то это может быть вашим виновником. Лучше всего создать какой-нибудь тестовый комплект, который может получить среднюю производительность для каждой из используемых вами инструкций.

Другой вопрос - влияет ли увеличение или уменьшение точности операторов (т. Е. С помощью двойных значений, а не синглов) на производительность?

0 голосов
/ 06 октября 2009

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

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