умножение в 20 раз быстрее деления!

Раздел, посвящённый самому важному - скорости.

Re: умножение в 20 раз быстрее деления!

Сообщение Paul Siberdt 15 апр 2011, 17:08

Если вы часто в рассчетах обращаетесь к размерности массива, имеет смысл вынести его в переменную, ибо
var1 = var2 работает вдвое (вчетверо для динамических массивов) быстрее, чем var1 = arr.length :)

Скорость запроса не зависит от числа элементов в массиве.
Аватара пользователя
Paul Siberdt
Адепт
 
Сообщения: 5317
Зарегистрирован: 20 июн 2009, 21:24
Откуда: Moscow, Russia
Skype: siberdt
  • Сайт

Re: умножение в 20 раз быстрее деления!

Сообщение alexz 15 апр 2011, 19:20

Как работают всякие левые JIT-компиляторы, я не в курсе, но майкрософтовский компилятор такие вещи обычно оптимизирует.

Исходный цикл:
Код: Выделить всё
for (int i = 0; i < ints.Length; i++)
{
        ints[i] = 0x12345678;
}


Оптимизированный нативный код, в который наш цикл компилируется:
Код: Выделить всё
00000067  xor         edx,edx                     ; обнуляем edx, в этом регистре будет хранится текущий индекс в массиве
00000069  mov         eax,dword ptr [ebx+4]       ; загружаем в eax длину массива
0000006c  test        eax,eax                     ; проверяем содержимое eax:
0000006e  jle         0000007D                      ; если в eax был ноль, т.е. массив нулевой длины, то выходим из цикла
00000070  mov         dword ptr [ebx+edx*4+8],12345678h     ; записываем в текущий элемент массива число 12345678h
00000078  inc         edx                         ; увеличиваем текущий индекс в массиве на единицу
00000079  cmp         eax,edx                     ; сравниваем длину массива с текущим индексом:
0000007b  jg          00000070                      ; если длина массива больше текущего индекса, то снова возвращаемся к выполнению тела цикла
0000007d


Отсюда видно, что длина массива считывается всего один раз в один из регистров процессора и всё время в нём хранится.

100 миллионов итераций цикла for (int i = 0; i < ints.Length; i++)
243 миллисекунд
234 мс
241 мс
245 мс


100 миллионов итераций цикла for (int i = 0; i < LENGTH; i++)
236 миллисекунд
257 мс
240 мс
239 мс
alexz
UNITрон
 
Сообщения: 270
Зарегистрирован: 16 ноя 2010, 23:37

Re: умножение в 20 раз быстрее деления!

Сообщение Paul Siberdt 15 апр 2011, 19:42

Выходит, внутренний добилдовый тест в движке мало имеет общего с производительностью в релизе и bottleneck искать надо в более широких вещах? :)

Вот еще что обнаружил:
Обычное обращение к компоненту вектора myVector.x почти вдвое быстрее, обращения по myVector[0]
Аватара пользователя
Paul Siberdt
Адепт
 
Сообщения: 5317
Зарегистрирован: 20 июн 2009, 21:24
Откуда: Moscow, Russia
Skype: siberdt
  • Сайт

Re: умножение в 20 раз быстрее деления!

Сообщение alexz 15 апр 2011, 20:00

Paul Siberdt писал(а):Выходит, внутренний добилдовый тест в движке мало имеет общего с производительностью в релизе и bottleneck искать надо в более широких вещах? :)

По крайней мере debug-билд, release-билд и release-билд, запущенный не из-под Студии, показывают разные результаты. Чтобы узнать, как будет выглядеть код в дикой природе, надо запускать его не из-под Студии и потом подключаться к нему студийным отладчиком.

Как повторить эксперимент, используя Mono, а не .Net, я не знаю. Возможно, что под Mono результат будет другой.

Недавно вынес часть кода, генерировавшего ландшафты, в отдельную библиотеку, прошёлся по ней нормальным профайлером, и он мне наглядно показал, какие места в программе можно и нужно ускорить. Замена алгоритма в одном из методов уменьшила время его выполнения с 30 секунд до 0,5. Ещё пару методов ускорил в несколько раз, просто уменьшив до достаточного разрешение используемых там карт и масок.

Вот еще что обнаружил:
Обычное обращение к компоненту вектора myVector.x почти вдвое быстрее, обращения по myVector[0]

x ― это простое поле класса, а [..] ― это целый метод с таким кодом:
Синтаксис:
Используется csharp
public float get_Item(int index)
{
    switch (index)
    {
        case 0:
            return this.x;

        case 1:
            return this.y;

        case 2:
            return this.z;
    }
    throw new IndexOutOfRangeException("Invalid Vector3 index!");
}


PS
Повторил тест с циклом на списке List<int> вместо массива int[]. Ускорение получилось, но всего примерно на 20 миллисекунд за 100 миллионов итераций.
alexz
UNITрон
 
Сообщения: 270
Зарегистрирован: 16 ноя 2010, 23:37

Re: умножение в 20 раз быстрее деления!

Сообщение Paul Siberdt 15 апр 2011, 20:21

Угу, спасибо... придется серьезнее бороться с бзиком "отыграть 0.1mc с пары тысяч операций" :)
Аватара пользователя
Paul Siberdt
Адепт
 
Сообщения: 5317
Зарегистрирован: 20 июн 2009, 21:24
Откуда: Moscow, Russia
Skype: siberdt
  • Сайт

Re: умножение в 20 раз быстрее деления!

Сообщение alexz 15 апр 2011, 21:19

Посмотрел на название темы и решил тоже проверить. Код типа такого:
Синтаксис:
Используется csharp
var numbers = new float[LENGTH];
const float K = 3f;

for (int i = 0; i < numbers.Length; i++)
{
        numbers[i] = numbers[i] / K;
}


ВЕЩЕСТВЕННЫЕ ЧИСЛА

Умножение элементов массива на константу ― 230 мс на 100 миллионов итераций
Код: Выделить всё
00000067  xor         edx,edx
00000069  mov         eax,dword ptr [edi+4]
0000006c  test        eax,eax
0000006e  jle         00000083
00000070  fld         dword ptr [edi+edx*4+8]
00000074  fmul        dword ptr ds:[007D01D0h]
0000007a  fstp        dword ptr [edi+edx*4+8]
0000007e  inc         edx
0000007f  cmp         eax,edx
00000081  jg          00000070
00000083


Умножение элементов массива на локальную переменную ― 230 мс на 100 миллионов итераций
Код: Выделить всё
00000072  xor         edx,edx
00000074  mov         eax,dword ptr [edi+4]
00000077  test        eax,eax
00000079  jle         0000008B
0000007b  fld         dword ptr [edi+edx*4+8]
0000007f  fmul        dword ptr [ebp-18h]
00000082  fstp        dword ptr [edi+edx*4+8]
00000086  inc         edx
00000087  cmp         eax,edx
00000089  jg          0000007B
0000008b


Деление элементов массива на константу ― 250 мс на 100 миллионов итераций
Код: Выделить всё
00000067  xor         edx,edx
00000069  mov         eax,dword ptr [edi+4]
0000006c  test        eax,eax
0000006e  jle         00000083
00000070  fld         dword ptr [edi+edx*4+8]
00000074  fdiv        dword ptr ds:[006501D0h]
0000007a  fstp        dword ptr [edi+edx*4+8]
0000007e  inc         edx
0000007f  cmp         eax,edx
00000081  jg          00000070
00000083


Деление элементов массива на локальную переменную ― 250 мс на 100 миллионов итераций
Код: Выделить всё
00000072  xor         edx,edx
00000074  mov         eax,dword ptr [edi+4]
00000077  test        eax,eax
00000079  jle         0000008B
0000007b  fld         dword ptr [edi+edx*4+8]
0000007f  fdiv        dword ptr [ebp-18h]
00000082  fstp        dword ptr [edi+edx*4+8]
00000086  inc         edx
00000087  cmp         eax,edx
00000089  jg          0000007B
0000008b


С одной стороны видно, что компилятор не стал заменять деление умножением даже в случае констант, хотя мог бы. С другой стороны разница в скорости невелика: ~20 мс на 100 миллионов итераций.
alexz
UNITрон
 
Сообщения: 270
Зарегистрирован: 16 ноя 2010, 23:37

Re: умножение в 20 раз быстрее деления!

Сообщение Neodrop 15 апр 2011, 21:37

Мда. Некропостинг, конечно. Никто не заметил, для какой версии Unity и MONO эта тема была создана ?
И тесты проведены на MONO или на Microsoft .NET ?
Это немного разные вещи и производительность .NET никого не волнует, потому что всё равно нам с ним работать не приходится.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Пред.

Вернуться в Оптимизация

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1