Оптимизация кода в j2me, является очень важной деталью, особенно когда приложение должно показывать большую частоту обновления экрана на обширном количестве телефонов. В java играх это часто необходимо для: 1. моделирования физики. 2. растеризации 3D графики с наложением текстур. 3. применения алгоритмов поиска, сортировки. 4. решения сложных и ресурсоемких задач. Есть некоторые хитрые способы, которые могут "заставить" работать приложение быстрее, и при этом жертвовать функционалом, упрощая некоторые аспекты программы, не придется. Рекомендации по оптимизации Очевидное: 1. new - является самой продолжительной по времени командой, поэтому её лучше не использовать часто во время игрового процесса (или real-time), а лучше вообще не использовать, если игра не пошаговая. 2. Вызов процедуры занимает время, математические процедуры лучше встраивать в код (inline) Пример:
int _x1 = 10; int _x2 = 20; int _y1 = 10; int _y2 = 20; int dot(int x1, int y1,int x2,int y2) { return x1 * x2 + y1 * y2; } //вариант 1 void main_loop() { int dot_ = dot(_x1, _y1, _x2, _y2);... } //вариант 2 void main_loop() { int dot_ = _x1 * _x2 + _y1 * _y2;... }
Разумеется, что вариант 2 работает быстрее. Не очевидное: 1. static функции вызываются быстрее остальных, поэтому, если есть возможность нужно делать все функции static. 2. Локальные переменные работают быстрее, чем глобальные. Если в процедуре часто (хотя бы больше 3-х раз) используется глобальная переменная, то её лучше присвоить локальной переменной Пример:
int type = 10; //вариант 1 void draw() { int a = type>>1; int b = type<<1; int mas[] = new int[2]; mas[0] = type; mas[1] = type; } //вариант 2 void draw() { int c = type; int a = c>>1; int b = c<<1; int mas[] = new int[2]; mas[0] = c; mas[1] = c; }
Вариант 2 будет работать быстрее, если процедура используется достаточно, часто, то имеет смысл, использовать данную оптимизацию. 3. С глобальными массивами та же ситуация. Пример:
//есть буфер экрана массив display размерностью 240 * 320int display[] = new int[240 * 320]; //вариант 1 void bg(int color) { for(int i=0; i<display.leght; i++) display[i] = color; } //вариант 2 void bg(int color) { int d[] = display; for(int i=0; i<display.leght; i++) d[i] = color; }
В данной ситуации вариант 2будет работать существенно быстрее, чем 1, т.к. необходимо будет закрасить все 76800 пикселей 4. Любое обращение к массиву всегда проходит проверку на выход за его границы. Значение индекса должно быть меньше длины массива и больше нуля, в противном случае будет вызвано исключение (то есть ошибка). Действия по проверке естественно занимают время. В данном случае (да и в остальных) мы платим за стабильность, скоростью. Пример:
//вариант 1 a = mas[0]; // +время за обращение b = mas[0]; // + время за обращение //вариант 2 int c = mas[0]; // + время за обращение a = c; b = c;
Вариант 2 будет работать быстрее, даже создание переменной 'c' не делает второй вариант медленнее. Если количество присваиваний к элементу массива больше 2-х, то данная оптимизация будет на много существеннее остальных. Время работы команд и данных: Ниже я привёл статистические данные по операциям над переменными. Каждая из операций была пропущена через цикл из 1000000итераций. Цифры получены с Nokia N73. Разумеется, что на других телефонах будут другие цифры, но отношения одних операций к другим должно сохраниться примерно прежним. Тип данных - byte # Код время 1 a>b 5 2 a<b 11 3 a += b 19 4 a -= b 19 5 a |= b 19 6 a &= b 19 7 a =~ b 19 8 a = -b 19 9 a >>= b 27 10 a <<= b 28 11 a *= b 30 12 a = b 47 13 a /= b 265 14 a %= b 265 15 mas = new byte[1] 440 16 mas = new byte[5] 530 17 mas = new byte[10] 639 Тип данных - short # Код время 1 a>b 5 2 a<b 11 3 a += b 19 4 a -= b 19 5 a |= b 19 6 a &= b 19 7 a =~ b 19 8 a = -b 19 9 a >>= b 27 10 a <<= b 27 11 a *= b 30 12 a = b 47 13 a /= b 265 14 a %= b 265 15mas = new short[1] 480 16 mas = new short[5] 708 17 mas = new short[10] 933 Тип данных - int # Код время 1 a>b 7 2 a += b 9 3 a -= b 9 4 a |= b 9 5 a &= b 9 6 a =~ b 9 7 a = -b 9 8 a<b 10 9 a *= b 14 10 a >>= b 19 11 a <<= b 19 12 a = b 46 13 a /= b 250 14 a %= b 250 15 mas = new int[1] 442 16 mas = new int[5] 854 17 mas = new int[10] 1444 Тип данных - long # Код время 1 a += b 19 2 a -= b 19 3 a |= b 19 4 a &= b 19 5 a =~ b 19 6 a = -b 19 7 a>b 23 8 a<b 23 9 a *= b 41 10 a = b 59 11 a >>= b 59 12 a <<= b 59 13 mas = new long[1] 582 14 a %= b 682 15 a /= b 742 16 mas = new long[5] 1602 17 mas = new long[10] 2976 Тип данных - float # Код время 1 a = b 45 2 a>b 124 3 a<b 130 4 a *= b 171 5 a /= b 230 6 a = -b 333 7 a += b 426 8 a -= b 442 9 mas = new float [1] 442 10 mas = new float [5] 844 11 mas = new float [10] 1440 12 a %= b 2544 13 a = b 45 Тип данных - double # Код время 1 a = b 60 2 a>b 144 3 a<b 253 4 a *= b 361 5 a /= b 390 6 mas = new double [1] 574 7 a %= b 452 8 a += b 543 9 a -= b 578 10 mas = new double [5] 1496 11 mas = new double [10] 2747 Вызовы процедур. Процедуры # Код время 1 Math.max(a,b) <5 2 a++, a- <5 3 Math.abs(b) <5 4 b > 0 ? b : -b 90 5 static void F() 178 6 static int F1() 181 7 static void F1(int a) 192 8 static void F2(int a,int b,int c,int d,int p,int p1) 219 9 System.currentTimeMillis() 8817 10 Math.sin(b) 17507 Процедуры Math.abs, Math.max, Math.min самые быстрые, конструкции типа b > 0 ? b : -b или вызов функции будут работать медленнее. Системное копирование массивов # Код время 1 System.arraycopy(b,0,a,0,5); 1648 2 System.arraycopy(b,0,a,0,12); 1712 3 System.arraycopy(b,0,a,0,100); 3546 4 for(j=0;j<5;j++) a[i] = b[i]; 814 5 for(i=0;i<12;i++) a[i] = b[i]; 1795 6 for(i=0;i<100;i++) a[i] = b[i]; 14408 По таблице видно, что использовать System.arraycopy оптимальнее если нужно скопировать больше 11переменных типа int (или 44байта). Тесты под номерами 3и 6 наглядно показывают прирост производительности, при использовании System.arraycopy, почти в 5 раз.
|