Векторные математические функции для S-кривой

Мне нужен лучший способ расчета S-образной кривой, чем метод, описанный ниже. Я использую его для рисования S-образной кривой в методе drawRect, а также для вычисления легкости увеличения/уменьшения громкости музыкального файла для затухания.

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

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

3 * position * (1 - position) * (1 - position) * firstControlPoint + 3 *
position * position * (1 - position) * secondControlPoint +
position * position * position * 1.0;

Где firstControlPoint равно 0,0, а secondControlPoint равно 1,0.


person Anthony Myatt    schedule 17.11.2013    source источник


Ответы (2)


Вам может быть интересна эта статья о даже быстрее Безье, но 100 вычислений этого не много. Я выполнял тысячи таких вычислений для каждого кадра на iPad первого поколения. Для такого небольшого набора вы вряд ли получите большую пользу от Accelerate. (и Accelerate может быть намного медленнее, чем простой C для небольших наборов данных).

Однако есть несколько вещей, которые следует учитывать:

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

  • Если это устройство NEON (например, iPhone или iPad), внутренние особенности почти никогда не бывают выигрышными (это может измениться, когда Clang становится лучше, но это было мое открытие в 2012 году). Ручной код NEON определенно может быть выигрышным, если вам действительно нужна производительность, но это головная боль для программирования, поэтому я бы сначала посмотрел где-нибудь еще. Программирование на ассемблере радикально отличается от программирования на C. Если бы вы могли просто добавить встроенную функцию в определенный момент и заставить ее работать быстрее, компилятор уже сделал бы это (и на самом деле он это делает).

  • Если вы выполняете вычисления с плавающей запятой и вам просто нужно, чтобы результаты были «почти точно правильными и идеальными для рисования и анимации», а не «абсолютно правильными и воспроизводимыми в соответствии с правилами IEEE», вам следует включить быструю математику. Самый простой способ сделать это — переключить оптимизацию компилятора с «Самая быстрая, наименьшая» на «Самая быстрая, агрессивная оптимизация». Трудно представить случай, когда это неправильная настройка для приложений iOS и почти всегда правильная настройка для приложений Mac. Этот параметр также включает дополнительную векторизацию, которая может иметь большое значение для ваших циклов.

  • Вам обязательно стоит взглянуть на статью «Оптимизируйте свой код с помощью LLVM» с WWDC 2013. В нем рассказывается, как структурировать ваш код, чтобы компилятор помог вам больше всего. Вы также можете посмотреть на The Accelerate Framework из тех же видеороликов, но маловероятно, что Accelerate является подходящим инструментом для решения этой проблемы.

  • Вместо того, чтобы вычислять это самостоятельно, рассмотрите возможность использования CAPropertyAnimation с пользовательской функцией синхронизации. Их можно использовать для установки любого значения; не только анимации слоев. Для рисования кривой рассмотрите возможность использования UIBezierPath вместо ручного расчета кривой.

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

person Rob Napier    schedule 17.11.2013

Ваша S-кривая — это кривая Безье, поэтому вы можете использовать алгоритм Де Кастельжау.

q0 = t * p0 + (1 - t) * p1
q1 = t * p1 + (1 - t) * p2
q2 = t * p2 + (1 - t) * p3
r0 = t * q0 + (1 - t) * q1
r1 = t * q1 + (1 - t) * q2
s0 = t * r0 + (1 - t) * r1

Затем вы можете использовать SSE/AVX-внутренности для вычисления кратных кривых (2 -> 128 бит, 4 -> 256 бит) с одним потоком инструкций.

person matovitch    schedule 17.11.2013