Названия переменных в динамических ЯП

Известная многим фишка оптимизации при сравнении длин векторов, вкратце расскажу про неё.
Пусть у нас есть 3d-вектора. Многие помнят как вычислять его длину - sqrt(x2+y2+z2). Часто в коде надо сравнивать длины векторов (найти самого близкого противника и тп).
Код вида Point3d direction1, direction2; direction1.length() < direction2.legnth()) вычисляет два квадратных корня, можно смело заменять его на direction1.lengthSquared () < direction2.lengthSquared ()), где lengthSquared не извлекает корень из x2+y2+z2. Что может быть раз в 10 быстрей.
2. Сюрприз при скриптинге. 100 тактов FPU против хеш-таблиц. .
В нашем движке, экспортированные из C++ для python 3d-вектора имеют методы length и lengthSquared. И в документации постулируется, что лучше вызывать lengthSquared когда надо сравнить длины векторов, типа это быстрей и корень не извлекается. Проверил это, и получил сюрприз - lengthSquared оказался на 5% медленней чем length. Объяснение простое - все поля/методы объекта в python это строки в хеш-таблице. Строка "lengthSquared" длинней чем "length". И суета с длиной идентификатора в хеш-таблице оказалась важней чем наличие квадратного корня. Вывод - в динамическом ЯП неважно что звать, вычисление "сложного" синуса или тупое сложение. Если хотим выжать сколько-то (десятков) процентов скорости не переписывая на C - стоит класть методы объектов в локальные переменные, ls = Point3d.lengthSquared; ls(dir1) < ls(dir2);
3. Сюрприз при скриптинге - 2. Глобальные переменные против локальных. .
Доступ к глобальной переменной в питоне - достаточно долгая операция. Питону нужно проверить пачку вложеных неймспейсов, что бы найти глобальную переменную, причем по строковому имени.
Доступ к локальным переменным - гораздо быстрей, без поисков по хеш-таблицам ( строка превращается в индекс при генерации байт-кода ).
У нас много всяких Enum, часто код насыщен использованием enum-типа вроде
Moving.Direction.NORTH, Moving.Direction.SOUTH, Moving.Direction.EAST, Moving.Direction.WEST.
После присваивания Moving.Direction в локальную переменную
Dir = Moving.Direction; Dir.NORTH, Dir.SOUTH, Dir.EAST, Dir.WEST
опять же выигрывал десятки процентов по производительности.
Разрушением картины миры для моего коллеги так же стало, что код при переписывании со строк на enum ( напр. MoveObject("EAST") на MoveObject(Moving.Direction.EAST)) замедлился. У него после C++ в голове не укладывалось, что использование строк может быть быстрее, чем целочисленного enum. А причина была именно в том, что Moving.Direction.EAST - это аж три поиска строк по хеш-таблицам, причем первый - в глобальном namespace с сотнями глобальных имен.
По той же причине for i in range(1000*1000): j = i*i перенесённый из глобальной области в функцию тоже может сильно ускориться (переменные i,j из глобальных станут локальными).
|
</> |