LINUX.ORG.RU

Ускорить функцию

 ,


1

2

Привет, ЛОР.

Оптимизирую функцию. Удалось снизить время выполнения 100 000 000 итераций с 58 секунд до 32. Дальше пока не лезет. Глянете? Может кто еще какой-нибудь финт сможет предложить: http://paste.org.ru/?iv1p2w

★★

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

uuwaan ★★
()

хотябы квадраты вычислить один раз...

anonymous
()

Второе — это посчитать общие члены выражений один раз и записать в константную переменную. SML_SQR(line.p2.x), SML_SQR(line.p2.y), line.p2.y * target.y встречаются по тексту по несколько раз, а вычисляются каждый раз заново.

uuwaan ★★
()

Про аргументы сказали. Вопрос (к сабжу значения не имеет): такой вид комментирования присутствует во всем проекте?

comp00 ★★★★
()
Ответ на: комментарий от uuwaan

Как минимум не передавать аргументы функции по значению.

Я для ускорения вычислений модифицирую p2 и target. Поэтому придется снимать копию. Что приводит к тому же, откуда пришли.

sambist ★★
() автор топика
Ответ на: комментарий от uuwaan

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

sambist ★★
() автор топика
Ответ на: комментарий от sambist

думаю уже на 2м уровне вложенности там будет труба с читаемостью. Но зато алгоритм понятен, да.

comp00 ★★★★
()
Ответ на: комментарий от sambist

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

uuwaan ★★
()
Ответ на: комментарий от sambist

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

100% не будет.

uuwaan ★★
()
Ответ на: комментарий от hateyoufeel

За меня это делает компилятор:

void test()
{
    SmlLine line =
            (SmlLine){(SmlPoint){rand(), rand()},
                      (SmlPoint){rand(), rand()}};
    SmlPoint point = (SmlPoint){rand(), rand()};

    SmlGradientValue(line, point);
}

sambist ★★
() автор топика
Ответ на: комментарий от uuwaan

100% не будет.

Да ладно, cache miss мимо обоих аргументов вполне может выйти в лишние пару-тройку сотен циклов.

hateyoufeel ★★★★★
()
Ответ на: комментарий от sambist

Да выше уже насоветовали оп основным пунктам. Судя по тому, что «Вопрос в том, не будет ли время выделения переменной больше времени одного умножения, но это уже тесты покажут.», тут нужна хорошая лекция.

А поскольку проект проприетарный, как я понял, — коммерческие проекты бесплатно я не консультирую.

Deleted
()
Ответ на: комментарий от sambist
void test()
{
    SmlLine line =
            (SmlLine){(SmlPoint){rand(), rand()},
                      (SmlPoint){rand(), rand()}};
    SmlPoint point = (SmlPoint){rand(), rand()};

    SmlGradientValue(line, point);
}

И сколько в этой тестовой функции реально занимает вычисление SmlGradientValue(), а сколько — вызов 6-ти randr()?

Deleted
()
Ответ на: комментарий от sambist

Прям интересно стало. К чему такая информированность каждой строки кода? Учебный проект? Преподаете какие нибудь алгоритмы компьютерной графики?

comp00 ★★★★
()
Ответ на: комментарий от Deleted

А поскольку проект проприетарный, как я понял,

А на это вас натолкнул файл LGPL на скриншоте. Ну ок, чо.

sambist ★★
() автор топика
Ответ на: комментарий от comp00

Ни то ни другое. Этот проект - хобби. На работе одни огурцы, дома другие. Так вот эти схемы это то же самое когда мы зачищаете бархатом деревянную деталь после шкурки нулевки. Ощущения те же самые.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

А на это вас натолкнул файл LGPL на скриншоте.

А, ну слепой, значит, не видел. Натолкнуло отсутствие релевантных ссылок в гугле.

Как объявлены структуры?

Deleted
()
Ответ на: комментарий от Deleted

И сколько в этой тестовой функции реально занимает вычисление SmlGradientValue(), а сколько — вызов 6-ти randr()?

 Nul function: 100 000 000 cycle took 30.270328 s
Full function: 100 000 000 cycle took 32.362579 s
sambist ★★
() автор топика
Ответ на: комментарий от Deleted

Натолкнуло отсутствие релевантных ссылок в гугле.

А я должен о нем кричать во все стороны?

Как объявлены структуры?

/* E */ typedef struct
{
    int32_t x;
    int32_t y;
} SmlPoint;

/* E */ typedef struct
{
    SmlPoint p1;
    SmlPoint p2;
} SmlRect;

/* E */ typedef SmlRect SmlLine;
sambist ★★
() автор топика

Алгоритм по ссылке какой-то очень сложный.
Я бы развернул систему координат так, чтобы отрезок AB лёг строго на ось OX.
При этом проекция D точки T будет получаться просто отбрасыванием координаты y.

Поворот на угол L выполняется по формулам

x_new = x_old * cos(L) - y_old * sin(L)
y_new = x_old * sin(L) + y_old * cos(L)


В наших целях принимаем cos(L)=AB.x, sin(L)=-AB.y. Это не-отнормированные значения синуса и косинуса, ну да и хрен бы с ними. Важно только то, что:
x_new = x_old * AB.x + y_old * AB.y


Код не компилировал и не запускал, выкладки все чисто умозрительные. Подвержено signed overflow.

short Grad(const Point &a, const Point &b, const Point &t)
{
int64 ab_x = b.x - a.x;
int64 ab_y = b.y - a.y;
int64 new_ab_x = ab_x * ab_x + ab_y * ab_y;
int64 new_at_x = (t.x - a.x) * ab_x + (t.y - a.y) * ab_y;
if(new_at_x <= 0) return 0;
if(new_at_x >= new_ab_x) return 255;
return 255 * new_at_x / new_ab_x;
}

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от Manhunt

Вау. Вроде на compileonline проверил, пашет вроде как надо. Сейчас забью в код и засеку время.

sambist ★★
() автор топика
Ответ на: комментарий от Manhunt

Идея хорошая, но ускорения не получилось:

Manhunt's function: 100 000 000 cycle took 34.813816 s
Full function: 100 000 000 cycle took 32.362579 s

sambist ★★
() автор топика
Ответ на: комментарий от Manhunt

ыыы! В общем int64 там не нужен. 1) Он жрет время, 2) Чтобы добиться переполнения нужны текстуры настолько больших размеров, что они не влезают в память.

Поменял на int32. В общем вы волшебник.

int32 function: 100 000 000 cycle took 31.315514 s

sambist ★★
() автор топика

Manhunt интересный способ предложил. Но я на другое хотел обратить внимание, пригодится на будущее. Вот ты пишешь:

    if ((line.p1.x == line.p2.x) &&
        (line.p1.y == line.p2.y))
        return 0;

    line.p2.x -= line.p1.x; line.p2.y -= line.p1.y;

Здесь у тебя дважды вычисляется разность line.p2.x - line.p1.x (один раз при сравнении, второй раз при выполнении -= ), дважды вычисляется разность line.p2.y - line.p1.y и выполняется до двух условных переходов.

Компилятор не такой умный, чтобы это оптимизировать. Вот что получается:

        cmpl    %eax, %ebx
        jne     .L2
        cmpl    %edx, %esi
        je      .L8
.L2:
        subl    %eax, %ebx
        subl    %edx, %esi

Как это переписать короче и читабельнее? Примерно так:

    int32_t dx = line.p2.x - line.p1.x;
    int32_t dy = line.p2.y - line.p1.y;
    if (dx | dy == 0)
        return 0;

Результат:

        subl    %edx, %ebx
        sete    %cl
        subl    48(%esp), %eax
        orl     %eax, %ecx
        je      .L2

Что касается переменных, у компилятора нет «стоимости создания переменных». Всё вычисляется в регистрах. При недостатке регистров — сохраняется/загружается из стэкового фрейма. Цена создания фрейма стека константная: одно вычитание при входе в функцию и одно сложение при выходе.

Deleted
()

Вопрос такой, что за SML_SQR?

grondek
()

Лень читать тред, мб уже сказали, так что не бейте за повторение:

if (((int64_t)-SML_SQR(line.p2.x)   - SML_SQR(line.p2.y)) *
                 (-line.p2.x * target.x - line.p2.y * target.y) <= 0)
        return 0;

/* comments stripped */

if (((int64_t)-line.p2.x * target.x - line.p2.y * target.y +
                SML_SQR(line.p2.x)    + SML_SQR(line.p2.y)) <= 0)
        return 255;

Тут все по 2 раза вычисляется, хотя между условиями ничего не меняется. Можно один раз же посчитать квадраты, произведения ну и т.д. не?

maverik ★★
()
Ответ на: комментарий от Deleted

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

Raw function: 100 000 000 cycle took 58.118729 s
Int function: 100 000 000 cycle took 41.755593 s
sambist ★★
() автор топика
Ответ на: комментарий от maverik

И более того, далее в части

/* II & III */

использовать уже посчитаные значения.

maverik ★★
()

К моему вопросу.

Если SML_SQR( a ) = a * a, то

Dx = ((SML_SQR(line.p2.x) * target.x + line.p2.y  * line.p2.x * target.y)) /
                (SML_SQR(line.p2.y) + SML_SQR(line.p2.x));
return (unsigned char)(Dx * 255 / line.p2.x);

можно записать как

Dx = (unsigned char)(255 * (line.p2.x * target.x + line.p2.y * target.y)) /
                (SML_SQR(line.p2.y) + SML_SQR(line.p2.x)) );

И аналогично в другом условии. Избавляемся от пары умножений и деления.

grondek
()
Ответ на: комментарий от Deleted
int32_t dx = line.p2.x - line.p1.x;
int32_t dy = line.p2.y - line.p1.y;
if (dx | dy == 0)
    return 0;

Битовые операции с signed int? Хм, знатное извращение.

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

Битовые операции с signed int? Хм, знатное извращение.

А шо, инты уже имеют минус ноль, отличный от плюс ноля?

Deleted
()
Ответ на: комментарий от Deleted

А шо, инты уже имеют минус ноль, отличный от плюс ноля?

Это не имеет значения, поскольку по стандарту битовые операции с signed являются UB.

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

Это не имеет значения, поскольку по стандарту битовые операции с signed являются UB.

Здрасти, приехали.

Constraints

Each of the operands shall have integer type.

Semantics

The usual arithmetic conversions are performed on the operands.

The result of the | operator is the bitwise inclusive OR of the operands (that is, each bit in the result is set if and only if at least one of the corresponding bits in the converted operands is set).

Deleted
()
Ответ на: комментарий от Deleted

Здрасти, приехали.

Похоже я в очередной раз попутал с операциями сдвига signed.

andreyu ★★★★★
()
Ответ на: комментарий от Manhunt

Простите, а вы не могли бы подробнее рассказать, почему мы принимаем cos\sin равными таким значениям? Очень интересно.

sambist ★★
() автор топика
Ответ на: комментарий от CYB3R

Пфф. Только «анскильные лалки» (с) пишут на асме. Настоящие братюни сразу в машинных кодах.

maverik ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.