Показаны сообщения с ярлыком Программирование. Показать все сообщения
Показаны сообщения с ярлыком Программирование. Показать все сообщения

24 февр. 2012 г.

Приближённые вычисления арктангенса в MIDP 2.0

Как известно, объект java.lang.Math в MIDP 2.0 лишён функции atan(), как впрочем и многих других обратных тригонометрических функций. Однако арктангенс практически незаменим при вычислении значений углов прямоугольного треугольника по известным длинам его катетов. Ниже представлена одна из возможных реализаций функции арктангенса, дающая вполне хорошие результаты для аргументов в самом широком диапазоне. Следует лишь напомнить, что данная функция принимает в качестве аргумента величину отношения противолежащего катета прямоугольного треугольника к прилежащему, и возвращает значение соответствующего угла в радианах.


// Константы, необходимые для вычисления арктангенса
private static final double tanPI12 = Math.tan(Math.PI/12);
private static final double tanPI6 = Math.tan(Math.PI/6);
private static final double PI6 = Math.PI/6;
private static final double PI2 = Math.PI/2;

private static final double c1 = 1.6867629106;
private static final double c2 = 0.4378497304;
private static final double c3 = 1.6867633134;

/**
* Функция приближённого вычисления арктангенса.
*
* @param a Отношение противолежащего катета прямоугольного треугольника к прилежащему
* @return Значение соответствующего угла в радианах
*/
public static double atan(double a)
{
double result = 0;

boolean complement = false; // Принимает значение true если аргумент "a" больше 1.0
boolean region = false; // Принимает значение true если аргумент "a" больше значения Math.tan(Math.PI/12)
boolean sign = false; // Принимает значение true если аргумент "a" отрицательный

if (a < 0)
{
a = -a; // Дать аргументу "а" позитивное значение
sign = true; // Дать результату негативное значение
}

if (a > 1.0)
{
a = 1.0 / a; // Преобразовать аргумент "а" в значение от 0 до 1.0
complement = true;
}

if (a > tanPI12)
{
a = (a - tanPI6) / (1 + tanPI6 * a); // Ограничить аргумент "а" значениями от 0 до Math.tan(Math.PI/12)
region = true;
}

// Вычислить приблизительное значение арктангенса для значения "а" в диапазоне от 0 до Math.PI/12
result = a * (c1 + a * a * c2) / (a * a + c3);

if (region)
{
result += PI6; // Поправить значение угла, так как аргумент имел значение больше Math.tan(Math.PI/12)
}

if (complement)
{
result = PI2 - result; // Поправить значение угла, так как аргумент имел значение больше 1.0
}

if (sign)
{
result =- result; // Поправить значение угла, так как аргумент был негативным
}

return result;
}

12 сент. 2011 г.

Первые шаги в Андроиде

Только что запустил свой первый "Хэллоуворлд" на устройстве с Андроидом. По сравнению с телефонами, снабжёнными MIDP 2.0, Андроид кажется настоящим компьютером с предустановленным JDK. Чего только стоит список пакетов, доступных в Андроиде. Отдельно порадовал способ определения пользовательского интерфейса: каждая из "активностей" может снабжаться своим интерфейсом, написанным на HTML-подобном языке. Правда при желании разработчики могут создавать свои интерфейсы как угодно, хоть в OpenGL. Лишь бы было желание.

От других доступных библиотек просто теряется дар речи: HTTP, XML, криптография, графика, телефония, стандартные компоненты UI и масса утилит. Практически всё, что только может понадобиться любому разработчику мобильных приложений.

Ну, и для того чтобы начать программировать под Андроид, вам вполне хватит обычного компьютера, Эклипса и знания "Бейсика" наших дней — вездесущей Явы. Как же тут, скажите, можно удержаться, и не создать какое-нибудь приложение или игру?

13 июн. 2011 г.

Решение задач в "Пректе Эйлера"

Как я и предполагал, к вечеру голова уже перестала что-то соображать, а уровень креативности всё ещё зашкаливал. Видимо, сказалось действие выпитого за день особо чёрного программистского кофе, такого же чёрного как моя совесть как фон консоли в старом Юниксе.

Для успокоения непризнанного таланта программирования, из списка была выбрана самая первая, трудноразрешимая задача. Каким-то чудом на решение этой задачи у непризнанного гения программирования ушло примерно пять минут, три из которых непризнанный гений вспоминал как в Си задаётся операция вычисления модуля. Однако задача была успешно решена, и вот что обнаружилось.

Во-первых, "Проект Эйлера" для каждого своего участника создаёт миникартинку на которой указан ник участника, его страна, и количество успешно решённых задач. Чтобы поразить ваше воображение, представлю вам чудо-картинку своего профайла:


Картинка всем на зависть.

Во-вторых, после успешного решения какой-либо задачи, вам открывается pdf-файл, в котором вам подробно, и практически на пальцах рассказывается как об обычном способе решения задачи (я бы сказал, методом "в лоб"), так и о более тонких способах решения этой же самой задачи. Чтобы, так сказать, и эрудированность участников повысить, и чтобы самомнение непризнанных гениев программирования не превышало их текущего рейтинга в списке лидеров.

В комментарии к задаче авторы просто и доходчиво объясняют как можно реализовать ещё более простое и эффективное решение, что по-моему скромному мнению, и является вершиной мысли как в программировании, так и в любом другом виде деятельности человека. В общем и целом, я всячески восхищён этим проектом. По крайней мере, до тех пор, пока не доберусь до своей настоящей неразрешимой задачи под номером 2.

Проект Эйлера

Возможно, что все чёкнутые программисты уже в курсе, но я вчера открыл для себя весьма интересный "Проект Эйлера". Суть такова: вам, (чёкнутому программисту), предлагается решить более трёхсот математических задач при помощи программирования и имеющихся в вашем распоряжении среднестатистических вычислительных мощностей.

При этом вы можете использовать абсолютно любые способы и методы решения математических задач (хоть зарубки на полене или бухгалтерские счёты), главное — дать правильный численный ответ, и перейти к решению следующей задачи.

Скажу честно, что я ещё пока не решил ни одной задачи, но комбинация математики и программирования вашего покорного слугу завораживает как удав Каа — глупую мартышку.

Следует отметить, что часть вопросов была переведена энтузиастами на русский язык, и выложена на сайте euler.jakumo.org.

Тех же программистов, кто хочет отдохнуть от программирования, и кого больше интересуют математические задачи, возможно заинтересует ресурс MathsChallenge.Net, от которого, в своё время и отделился "Проект Эйлера".

За сим, как говорится, кланяюсь и желаю удачи в нелёгком деле усиления собственных творческих, мыслительных и вычислительных способностей.

8 июн. 2011 г.

Новообращение в Яву

Позади около пяти лет программирования в Symbian C++, но по иронии судьбы вот уже две недели как я являюсь новообращённым Ява-программистом. До этого, опять же по иронии судьбы, я пять лет работал то с JavaEE, то с JavaME то с JavaSE.

За те несколько лет, что я осваивал идиомы Symbian C++, Ява нисколько не постарела и даже не утратила своей актуальности. Язык по-прежнему живёт и развивается. Из нововведений в Яве стоить отметить разве что поддержку "типизированного кода" (generics), которая программистам на Си++ уже очень давно и очень хорошо известна в виде "шаблонов" (templates).

Однако так, глядишь, через 10 лет в Яве появится и поддержка беззнаковых (unsigned) чисел и возможности определения операторов для классов. Впрочем, если и без них Ява сумела составить достойную конкуренцию многим другим технологиям, то думаю она и в будущем обойдётся без этих мелочей, милых сердцу каждого адепта Си и Си++.

С непривычки, конечно же, немного поражает то огромное количество классов, библиотек и фреймворков, что доступны Ява-программистам. Разобраться во всём этом зоопарке сразу практически невозможно, но тут на помощь приходит старый и проверенный многими годами принцип: если вам, как говорится, нужен топор, то вам незачем браться за стамеску. Ведь по большому счёту, все проблемы программистов решаются при помощи избирательного минимализма и всем известного магического заклинания "RTFM".

2 мая 2011 г.

Опоздавшее рациональное предложение

Жаль, что в Симбиане и S60 нет никаких правил для наименования функций, связанных с передачей или получением прав на объекты, то есть касающихся так называемого "object ownership transferring".

К сожалению, многочисленные API в Симбиане изобилуют функциями, в документации которых нет ни слова о передаче или получении прав на используемые объекты. Простейшим примером такой функции может послужить метод RPointerArray::AppendL( const T* anEntry ). Ни в хидере для класса RPointerArray, ни в его официальной документации нет ни слова о том, что класс RPointerArray именно через метод AppendL() получает права на объект anEntry. В документации лишь просто заявлено, что класс RPointerArray принимает на себя обязательства по обеспечению жизнедеятельности объектов, на которые ссылаются хранимые в нём указатели.

Однако многие ли из программистов могут всегда хорошо помнить каким именно образом каждый из конкретных классов обращается с используемыми в нём указателями и ссылками на объекты? Многие ли из программистов имеют достаточно времени для проверок передачи прав на объекты в создаваемом и используемом ими коде?

Только покопавшись в примерах, статьях или книгах для разработчиков, можно найти упоминание о том, что именно в методе RPointerArray::AppendL() передаются права на указываемые объекты. И если о классе RPointerArray ещё как-то упоминается в многочисленных материалах, то о других, более экзотических классах, или библиотеках сторонних разработчиков, уже нет.

Для опытных "симбионщиков", это конечно же, далеко не новость, а вот для новичков — достаточно неприятный сюрприз, приводящий как к утечкам памяти, так и к двойным удалениям объектов.

А ведь казалось бы, что может быть проще, чем добавить к правилу именования L-методов (leaving methods) ещё одно правило, касающееся методов, в которых происходит передача прав на объекты? Например, имена таких методов могли бы заканчиваться на букву "O", от выражения "ownership transferring".

В таком случае метод RPointerArray::AppendL( const T* anEntry ) мог бы получить имя RPointerArray::AppendOL( const T* anEntry ). И тогда программист любой закалки и выучки сразу бы заметил, что в данном методе речь идет не только о возможности "сброса" (leave), но так же и о передаче (или получении) прав на объект anEntry.

Уверен, что это помогло бы предотвратить множество ошибок, связанных с указателями и ссылками на объекты. И хотя мои советы Симбиану не только не нужны, но уже и бесполезны, всё же у любого из разработчиков, создающих программы на Symbian C++, есть право и возможность облегчить жизнь себе и другим программистам. Естественно, если этот мой весьма скромный совет будет найден хоть немного полезным.

11 апр. 2011 г.

Настоящий программист

Настоящий программист никогда не ищет легких и проверенных путей. Изобретение велосипедов не только приятно, но и полезно для развития собственных умственных способностей.

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

Настоящий программист никогда не читает документацию по всем доступным библиотекам. Изобретение велосипедов - это еще и возможность профессионального роста в глазах коллег по проекту.

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

Настоящий программист во всём полагается на тестера. Даже когда тестер во всём положился на программиста.

Настоящий программист никогда не читает Кнута, Вирта, Дейкстру и многих других общепризнанных специалистов. Излишнее доверие к чужому опыту - верный признак неуверенности в собственных силах и умственных способностях.

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

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

Настоящий программист всегда остается настоящим программистом. Даже когда становится руководителем проекта или целого программистского отдела.

18 янв. 2011 г.

Про любимые языки программирования

По наводке друга послушал 222-й выпуск "Радио-Т" о различных языках программирования. Должен признаться, что к "Радио-Т" я отношусь совершенно нейтрально, и лично ожидал просто услышать хороший рассказ об особенностях того или иного языка программирования, однако увы, пришлось выслушать лишь байки ведущих о том, что язык Х хуже языка У, потому что язык Й их всех намного лучше.

Лишний раз с печалью наблюдал за спором программистов о том, чей язык программирования лучше. Это все равно что хирурги стали бы спорить о том, чей любимый хирургический инструмент лучше, и почему все операции нельзя выполнить лишь одной хирургической пилой.

За те более чем 10 лет, что я активно программирую и пытаюсь научиться программировать еще лучше, я неоднократно приходил к одному и тому же выводу: не существует хороших™ или плохих™ языков программирования; есть языки программирования, которые лучше или хуже справляются с теми или иными задачами, и всё.

Более того, если какой-нибудь программист безапеляционно заявляет, что язык Х лучше всех остальных, то скорее всего такой специалист знаком исключительно с языком Х, а про другие лишь читал в Википедии.

Более-менее грамотные специалисты прекрасно понимают, что языки программирования не живут сами по себе. Они вынашиваются, создаются, используются и изменяются людьми, и наследуют от последних все свои "закорючки" и "шероховатости". Однако несмотря на все свои достоинства и недостатки, главная задача любого языка программирования - это успешное решение конкретных задач, стоящих перед людьми в той или иной сфере их деятельности (пусть даже если этим языком программирования является Brainfuck).

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

3 сент. 2010 г.

Приближенные значения числа Пи

Сегодня я, по случаю пятницы, решил усомниться в правдивости открытий древних математиков, и решил перепроверить: так ли уж были хороши рациональные дроби, найденные древними для нахождения приблизительного значения числа Пи?

Таких рациональных дробей известно предостаточно. Взять хотя бы вычисления Архимеда. Древний мудрец вычислил, что число Пи больше 223 / 71 и меньше 22 / 7. Древние вавилоняне считали, что число Пи примерно равно 25 / 8. Древние египтяне представили свои варианты дробей: 22 / 7 и 256 / 81. Индийский гений Рамануджан, живший в начале прошлого века, определил число Пи как дробь 355 / 113.

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

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
double constantPi = 3.1415926535897932384626433832795028841971693993751;
double calculatedPi;
double minPi = 3.14149; // Нижняя граница точности наших вычислений.
double maxPi = 3.14160; // Верхняя граница точности наших вычислений.

cout.precision(50); // Выжмем из потока вывода все возможное и невозможное!

cout << "Constant Pi = " << constantPi << "\n" << endl; // Образец для подражания.

// Вычислим в пределах тысячи все подходящие числители и знаменатели.

for (int i = 2; i <= 1000; i++)
{
for (int j = 2; j <= 1000; j++)
{
calculatedPi = (double) j / i;

if (calculatedPi <= maxPi && calculatedPi >= minPi)
{
cout << j << " / " << i << " = " << calculatedPi << endl;
cout << "Constant - Calculated = " << constantPi - calculatedPi << "\n" << endl;
}
}
}

return 0;
}

В результате работы этой замечательной программы я получил следующие результаты:

Constant Pi = 3.141592653589793115997963468544185161590576171875

333 / 106 = 3.1415094339622640084996874065836891531944274902344
Constant - Calculated = 8.3219627529107498276061960496008396148681640625e-005

355 / 113 = 3.141592920353982520964564173482358455657958984375
Constant - Calculated = -2.667641894049666007049381732940673828125e-007

666 / 212 = 3.1415094339622640084996874065836891531944274902344
Constant - Calculated = 8.3219627529107498276061960496008396148681640625e-005

688 / 219 = 3.1415525114155249397640545794274657964706420898438
Constant - Calculated = 4.014217426817623390888911671936511993408203125e-005

710 / 226 = 3.141592920353982520964564173482358455657958984375
Constant - Calculated = -2.667641894049666007049381732940673828125e-007

999 / 318 = 3.1415094339622640084996874065836891531944274902344
Constant - Calculated = 8.3219627529107498276061960496008396148681640625e-005

И что же мы получаем? А получаем мы шесть замечательных дробей, которые каждый из нас теперь может взять в качестве приближенного значения числа Пи. При этом дроби 333 / 106, 666 / 212 и 999 / 318 по вполне понятным законам арифметики и квантовой механики дают один и тот же результат. То же самое касается и дробей 355 / 113 и 710 / 226.

Однако дробь 355 / 113 дает наилучший результат, лучше остальных приближая нас к заветному значению числа Пи. Стоит ли лишний раз говорить, что эту дробь открыл гениальный индус Рамануджан, проживший столь недолгую и трагичную жизнь?

А как же, вы скажете, быть с открытиями древних вавилонян, египтян, Архимеда, и всех остальных, неравнодушных к математике? Очень просто. Нужно всего лишь изменить критерии точности наших вычислений. Уверяю вас, что изменяя переменные minPi и maxPi мы легко сможем получить все те замечательные результаты, что уже знаем о приблизительных значениях числа Пи. А может быть, - кто знает? - вы откроете свою, неизвестную науке замечательную дробь, приближающую нас к значению числа Пи.

2 сент. 2010 г.

Цитата

Нашел в одной хорошей книжке замечательную цитату:

"One of the many consequences of the exponential power-versus-time curve in computing, and the corresponding pace of software development, is that 50% of what one knows becomes obsolete over every 18 months."


"The Art of Unix Programming", Eric Steven Raymond.


Для тех, кто не силен в языках:

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


"Искусство программирования под Unix", Эрик Стивен Рэймонд.



И невольно напрашивается уже обивший оскомину вывод: ну и как тут программисту можно не любить математику и алгоритмы?

12 мая 2010 г.

Компиляция библиотеки GMP под Windows при помощи MinGW

Библиотека GMP, или GNU Multi-Precision Library используется во многих программах, так или иначе связанных с точными вычислениями. Библиотека GMP содержит множество алгоритмов и функций, благодаря которым вы можете выполнять в своих программах основные математические операции с заданной точностью. К примеру, расчитать число π до десятитысячного знака. Так как код GMP написан на Си и ассемблере, компиляцию библиотеки приходится осуществлять вручную под конкретную платформу и используемый вами процессор.

Если вы работаете в Линуксе или на Юниксе, то проблем с компиляцией библиотеки GMP у вас практически не возникает. Инструменты компиляции и сборки GMP изначально ориентируются на Unix-подобные системы. Однако если вы работаете в Windows, то можете столкнуться с некоторыми сложностями. Ниже я привожу вариант компиляции данной библиотеки при помощи инструментов MinGW и MSYS. Справедливости ради стоит отметить, что сборку библиотеки GMP можно так же осуществить при помощи среды Cygwin, однако я предпочитаю использовать MSYS, которая после настройки позволяет легко компилировать в Windows многие библиотеки и проекты, созданные под Unix-подобные системы. Итак:

I. Настройка Windows для компиляции GMP

1. Cначала необходимо установить MinGW. Устанавливать MinGW лучше всего в папку C:\MinGW, так как в последствии у вас будет меньше проблем с настройкой других инструментов.

2. После установки MinGW, необходимо установить набор утилит MSYS. Устанавливать MSYS лучше всего в его папку "по умолчанию", то есть в C:\msys\1.0. При установке MSYS будет производиться дополнительная конфигурация, во время которой вас попросят указать местоположение MinGW (папка C:\MinGW в нашем случае).

3. Помимо MSYS-а вам так же понадобятся его дополнительные модули: MSYS DTK 1.0, MSYS flex, MSYS regex и MSYS bison. Все указанные библиотеки и модули вы распаковываете и просто копируете в папку C:\msys\1.0, где образуется ваша Unix-подобная среда. Хотя установка MSYS-а выглядит сложной, на самом деле никаких сложностей с установкой нет, а в итоге вы получаете очень удобную среду для компиляции библиотек и программ из Unix-подобных систем на машине с Windows.

4. После успешной установки MSYS-а, внутри папки C:\msys\1.0\home у вас должна появиться соответствующая домашняя директория пользователя. Вы можете переименовать эту домашнюю пользовательскую директорию в любое другое, удобное для вас имя. Я для удобства переименовал ее в username. Если вы не хотите ничего переименовывать, то в нижеприведенных командах используйте вместо username имя вашей домашней директории.

5. Теперь нужно скачать библиотеку GMP, и распаковать её в вашу домашнюю пользовательскую директорию, то есть в папку C:\msys\1.0\home\username.

Теперь, когда у вас появилась среда, можно приступить к компиляции библиотеки. Следует отметить, что MSYS имеет так же множество других полезных модулей, которые могут понадобиться для компиляции других библиотек и проектов, однако добавлять их можно по мере необходимости.

II. Компиляция библиотеки GMP под Си

1. Откройте папку C:\msys\1.0, и запустите MSYS при помощи файла msys.bat. После запуска убедитесь, что вы находитесь внутри своей домашней директории username.

2. Введите команду mkdir gmp. В эту папку будут помещаться скомпилированные модули библиотеки GMP, а так же её заголовочный файл.

3. Перейдите в папку с библиотекой GMP при помощи команды cd gmp-x.x.x

4. Введите команду ./configure --prefix=/home/username/gmp

5. После окончания конфигурации, введите команду make

6. Теперь, когда библиотека GMP скомпилирована, можно запустить её тестирование при помощи команды make check

7. Очистите компиляцию от ненужных файлов при помощи команды make clean

8. Теперь, когда конфигураци, компиляция, тестирование и очистка библиотеки GMP успешно завершены, скопируйте модули libgmp.a и libgmp.la из папки C:\msys\1.0\home\username\gmp\lib в папку C:\MinGW\lib, а заголовочный файл gmp.h из папки C:\msys\1.0\home\username\gmp\include - в папку C:\MinGW\include. Так же можно скопировать содержимое папки C:\msys\1.0\home\username\gmp\share\info в папку C:\MinGW\share\info.

Теперь ваша среда MinGW получила полноценную библиотеку GMP. Следует отметить, что получившиеся модули и заголовочный файл предназначены для использования в Си-программах. Конечно, никто не запрещает использование Си-библиотек внутри программ, написанных на Си++, однакое если вы хотите явно получить библиотеку для Си++, то на шаге №4 вам следует ввести команду ./configure --prefix=/home/username/gmp --enable-cxx, а все остальное - выполнить точно так же, как и в случае с библиотекой для Си. Единственное, что будет другим - это имена скомпилированных модулей и полученного заголовочного файла. В случае с библиотекой для Си++, они будут соответственно libgmpxx.a, libgmpxx.la и gmpxx.h.

30 апр. 2010 г.

Компиляция библиотеки Boost на вашем компьютере

Библиотека Boost является одним из самых популярных сборников утилит для программирования на Си++. Львиная доля утилит и полезных классов Boost-a находится в заголовочных файлах, поэтому для их использования вашему IDE достаточно лишь указать путь к папке с заголовками библиотеки. Однако Boost так же имеет ряд утилит, воспользоваться которыми вы сможете только после их компиляции. К таким утилитам отнесятся:

Boost.Filesystem
Boost.IOStreams
Boost.ProgramOptions
Boost.Python
Boost.Regex
Boost.Serialization
Boost.Signals
Boost.System
Boost.Thread
Boost.Wave
Boost.DateTime
Boost.Graph
Boost.Test

Именно с компиляцией утилит библиотеки Boost и возникает большинство проблем. Однако следуя нижеприведенным инструкциям вы избавитесь от большинства проблем, связанных с установкой Boost-а на вашем компьютере. Отмечу лишь, что инструкции касаются компиляции Boost-a при помощий дистрибутива MinGW. Итак:

1. Установите либо отдельный дистрибутив MinGW, либо IDE c MinGW.

2. Пропишите в системной переменной Path имеющийся у вас дистрибутив MinGW таким образом, чтобы компилятор gcc мог легко вызываться из командной строки.

3. Скачайте библиотеку Boost и утилиту Boost Jam на ваш компьютер.

4. Создайте папку C:\Boost, и распакуйте в нее библиотеку Boost.

5. В папку с распакованной библиотекой Boost (С:\Boost\boost_1_42_0 в моем случае) поместите файл bjam.exe, извлеченный из архива утилиты "Boost Jam".

6. Создайте системную переменную BOOST_ROOT, содержащей путь к папке с библиотекой Boost (C:\Boost\boost_1_42_0 в моем случае).

7. Откройте командную строку и войдите в папку с библиотекой Boost (C:\Boost\boost_1_42_0 в моем случае).

8. Введите команду bjam --toolset=gcc install, и наблюдайте за процессом компиляции библиотеки Boost.


После успешного завершения компиляции в папке библиотеки Boost (C:\Boost\boost_1_42_0 в моем случае) должны появиться две новые директории:

C:\Boost\include\boost-1_42
и
C:\Boost\lib

Первая будет содержать все заголовочные файлы библиотеки Boost, а вторая - lib-файлы скомпилированных утилит. Теперь вы без проблем сможете воспользоваться всеми утилитами библиотеки Boost.

Обратите внимание на то, что при использовании бесплатных IDE, таких как Code::Blocks или Bloodshed Dev-C++, вам придется дополнительно указать в настройках компилятора пути к двум вышеупомянутым папкам, а так же добавить в список имеющихся библиотек все скомпилированные вами lib-файлы библиотеки Boost.

21 апр. 2010 г.

Лекция от создателя Си++



Наконец-то довелось увидеть воочию и посетить лекцию небезызвестного Бьярне Строуструпа. Речь на лекции шла о новом стандарте Cи++ - C++0x, об основных особенностях которого Бъярне попытался нам рассказать в течении 2-х с небольшим часов. Обо всем он, естественно, нам рассказать не смог, но главные черты стандарта C++0x были представлены хорошо и кратко.

Итак, основная цель стандарта C++0x заключается в улучшении производительности Си++. Улучшение производительность "по Си++" - это либо повышение быстродействия кода, либо уменьшение количества требуемой им памяти.

Теперь в язык официально встроен новый тип ссылок - ссылки на временный объект, или rvalue. Ссылка на временный объект будет объявляться при помощи оператора тип&&. В предыдущих версиях стандарта Си++ временные объекты могли передаваться в функции лишь в качестве неизменяемых ссылок (тип const&). Теперь же, при помощи ссылок на временные объекты, любая функция сможет изменять их по своему усмотрению.

Таким образом создается база для "перемещений", а не "копирований" объектов. Т.е. если раньше функция возвращала временный объект, и нам для сохранения данных этого объекта приходилось его копировать в новый объект, то теперь, указав в качестве возвращаемого типа ссылку временного объекта (тип&&), мы получаем возможность избежать ресурсоемкого копирования.

В целях безопасности переменная никогда не будет рассматриваться в качестве ссылки на временный объект, если мы явно не воспользовались шаблонной функцией std::move<T>(). Только в этом случае для объектов данного класса будет задействовано "перемещение", а не "копирование".

Так же в новом стандарте было улучшено конструирование объектов. Теперь, например, можно использовать вложенные конструкторы, когда один конструктор объекта вызывает другой свой конструктор. А наследующие классы теперь получили возможность указывать конструкторы базового класса, которыми они хотели бы воспользоваться при своем создании.

Так же речь шла о новых встроенных типах Си++, однако обо всем, к сожалению, Бьярне рассказать так и не успел. Однако и того, что он нам успел показать, было достаточно для нескольких дней усваивания.

20 нояб. 2009 г.

"Искусство программирования. Том 1. Основные алгоритмы"

Наконец-то одолел первый том "Искусства программирования" Дональда Кнута. Первый том содержит всего две главы: "Основные понятия" и "Информационные структуры", однако количество и качество материала первого тома порою было значительно выше моего уровня восприятия, что, впрочем, вовсе не удивительно. Боюсь, что очень многие тонкости я так и оставил на страницах этой замечательной книги. Впрочем, невыполненные мною упражнения (даже легкие), так же свели на нет всю полноту восприятия книги. Утешает лишь то, что даже усвоенный материал значительно улучшил мое понимание важности грамотного использования структур данных. В особенности - двоичных деревьев. А так же расскрыл универсальную применимость деревьев в обработке данных. Надеюсь, Кнуту хватит времени и сил на дописание всех оставшихся томов "Искусства", а мне - времени и сил на прочтение и понимание хотя бы самых основных моментов его книг.

19 авг. 2009 г.

Советы по улучшению производительности программ для Symbian OS

Ниже приводится мой перевод одного из буклетов от издательства Symbian Press: "Советов по улучшению производительности программ", или "Performance Tips".


Содержание.

Что такое "производительность"?
Почему производительность столь важна?
Убийцы производительности

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

Разработка и тестирование на эмуляторе
Вы, ваш компилятор и ваш код

Не идите против компилятора
Выучите немного ассемблера

Маленькие советы

Избегайте вызова функций внутри определения условия циклов
Используйте ссылки или указатели там, где это нужно
Не раскручивайте циклы
Избегайте длинных блоков из if и else
Используйте соответствующим образом квалификатор const
Быстрый анализ кода при помощи User::FastCounter()

Инструменты: пошаговый анализатор

Компиляция ROM-файла
Запуск анализатора
Запуск изучаемого кода
Остановка анализатора
Получение данных анализатора
Анализ данных
Копирование файла с данными
Создание графа активности
Выберите активную область и потоки
Создание листинга по всем функциям

Ресурсы для разработчиков






Что такое "производительность"?

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

Под "производительностью" понимается ряд характеристик, которые можно измерить во время работы устройства: время загрузки, размер ROM-памяти, загруженность ROM-памяти, вывод на экран изображений, время жизни батареи, и т.д. Предназначение устройства и наличие в нем ряда особенностей могут определить желаемые значения для представленных характеристик. Для достижения этих желаемых значений, программы смартфона должны быть спроектированы и реализованы подобающим образом.

Почему производительность столь важна?

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

Убийцы производительности

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

Код, повторяющийся в циклах

Излишние вычисления очень часто идут рука об руку с конструированием сложных типов. Обратите внимание на следующий пример:

// Метод, выполняющий некую простую операцию
ExampleClass::SimpleOperation( SimpleType a, SimpleType b )
{
// Создание ненужного сложного типа (см. текст)
ComplexType c = b.MakeComplex();

// Еще какой-нибудь код
}

// Выполнить сложные вычисления
ExampleClass::DoSomeComplexComputation( ... )
{
SimpleType a, b;

while ( moreToDo )
{
// Какой-нибудь код

SimpleOperation( a, b );

// Еще какой-нибудь код,
// который не изменяет переменную b
}
}

В вышеприведенном примере метод SimpleOperation(), выполняющий некую простую операцию, вызывается в цикле. При каждом выполнении тела цикла, вызывается метод SimpleOperation(), в котором от аргумента класса SimpleType создается один и тот же сложный объект класса ComplexType. При этом сложный тип никак не модифицируется в последующем коде. Такое цикличное создание ненужных объектов класса ComplexType понапрасну растрачивает ресурсы, и может привести к существенному снижению производительности кода. Если бы объект класса ComplexType можно было бы передать методу SimpleOperation() в качестве аргумента, его повторного создания можно было бы просто избежать:
 
ExampleClass::SimpleOperation( SimpleType a, ComplexType& b )
{
// Код простого метода
}

CExampleClass::DoSomeComplexComputation( ... )
{
SimpleType a;
ComplexType b; // Создать объект b в виде сложного типа

while ( moreToDo )
{
// Передать в функцию объект b в виде сложного типа ComplexType
SimpleOperation( a, b );

// Еще выполнить код
}
}

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

Неэффективное использование динамической памяти

Очень часто во встраиваемых системах динамическая память используется вместо стека. Без должного внимания, такая практика может привести к неумерным обращениям к "куче", так как стек обычно используется под временные переменные.

void LoopWithHeap( void )
{
while ( moreToDo )
{
CData* temp = new CData; // Создаем временную переменную на "куче"
GetData( temp ); // Инициализируем
ProcessData( temp ); // Используем
delete temp; // Удаляем
}
}

Всегда, где это возможно, временные переменные должны использоваться заново:

void LoopWithHeap( void )
{
CData* temp = new CData; // Создаем переменную на "куче"

while ( moreToDo )
{
GetData( temp ); // Инициализируем
ProcessData( temp ); // Используем
temp->Reset(); // Обнуляем
}

delete temp; // Удаляем в конце цикла
}

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

Другим возможным примером неразумного использования "кучи" является излишняя потребность кода в перераспределении динамической памяти. Плохо продуманный алгоритм программы может привести к постоянному выделению, освобождению и копированию ячеек динамической области памяти.

Ограниченное понимание возможностей библиотеки

Документация по API редко когда содержит подробные сведения о реализации той или иной компоненты. Создание кода на основе некорректно или плохо понятого API может привести к таким проблемам как двойная или нежелательная обработка и модификация данных.

Представьте себе класс, предоставляющий доступ к массиву и реализующий проверку границ его индексов в методе SetElement():

void ArrayClass::SetSize( int aSize )
{
iMaxLength = aSize;
}

void ArrayClass::SetElement( int aPos, unsigned char aChar )
{
if ( aPos >= 0 && aPos < iMaxLength )
{
iRawArray[ aPos ] = aChar;
}
}

А теперь представьте себе программу, которой нужно добавить некоторое число элементов в массив при помощи представленного выше класса:

void ExampleClass::FillArray()
{
// Какой-то код
myArray.SetSize( bytesToProcess );

for ( currentPos = 0; currentPos < bytesToProcess; currentPos++ )
{
myArray.SetElement( currentPos, aByte );
}
}

Неэффективность приведенного кода заключается здесь в ненужности проверки границ индексов массива внутри метода SetElement(), так как функция FillArray() устанавливает верхнюю границу для элементов массива внутри себя.

Подобные проблемы могут иметь множество источников. Возможно, разработчик класса ExampleClass не знал, что класс ArrayClass проверяет границы массива. Возможно, разработчик не знал об особенностях API класса ArrayClass. Возможно, что создатель класса ArrayClass не определил нужный нашему программисту API, так как не предполагал, что класс массива будет использоваться подобным образом.

Нежелательное приведение типов

При наличии плохого дизайна структур данных, время работы процессора бесполезным образом тратится на приведение типов. Приведение типов обычно носит статический характер, и выполняется для передачи данных внешним API.

Обратите внимание на пример приведения типа данных:

TInt intDrive;
TChar ch = ((*drives) [ i ])[ 0 ];
RFs::CharToDrive( ch, intDrive );
TDriveUnit curDrive( intDrive );

В коде необходимо использовать тип TDriveUnit, однако имя диска было сохранено в символьном виде, поэтому нам приходится трижды обрабатывать данные, прежде чем они станут пригодными для нашей программы. А теперь представьте, что данный код расположен в глубине интенсивно используемого цикла, и что большая часть работы цикла будет посвящена выполнению этих трех операций. По этому возможно более подходящим решением стало бы хранение отдельной переменной типа TDriveUnit, которую можно было бы прямо использовать в дальнейшем внутри цикла. Иногда проблема приведения типов может привести к размещению множества временных объектов в стеке программы, исключительно лишь в целях изменения интерфейса к конкретному объекту.

Обратите внимание на нижеприведенный пример, в котором мы можем наблюдать создание временных объектов только лишь ради вызова различных методов:

iDllEntry.iName.Des().Zero();
iDllEntry.iName.Des().Append( aPath.Drive() );
iDllEntry.iName.Des().Append( KSysBin );
iDllEntry.iName.Des().Append( *resourceName );
iDllEntry.iName.Des().Append( KDllExtension );

Пример продемонстрировал нам две проблемы. Главная проблема заключается в создании методом Des() временного объекта. В течении выполнения вышеизложенного года, создаются 5 временных объектов, создание которых можно было бы избежать при помощи одной локальной переменной:

TPtr des = iDllEntry.iName.Des();
des.Zero();
des.Append( aPath.Drive() );
des.Append( KSysBin );
des.Append( *resourceName );
des.Append( KDllExtension );

Менее заметная проблема заключается в используемых параметрах компилятора. Создатели метода Des() определили его как встраиваемый метод, однако если компилятору были даны указания на оптимизацию объемов производимого кода, то при частом вызове методов Des(), компилятор не станет встраивать эти методы, а займется оптимизацией их вызовов.

Неэффективное использование файлов

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

Неэффективность использования файлов может проявиться при использовании файловой системы в качестве базы данных, в которой файлам и директориям отводится роль структуры этой базы.

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

В следующем примере мы можем обнаружить другую проблему использования файлов, а именно - чтение данных в виде нескольких маленьких блоков:

EXPORT_C CColorList* ColorUtils::CreateSystemColorListL( RFs& aFs )
{

CDirectFileStore* store;
store = CDirectFileStore::OpenL( aFs, KGulColorSchemeFileName, EFileRead | EFileShareReadersOnly ));

RStoreReadStream stream;
stream.OpenL( *store, store->Root() );

CColorList* colorList = CColorList::NewLC();
stream >> *colorList;
return colorList;
}

Проблема здесь заключается в реализации перегруженного оператора ">>", чья реализация осуществляется методом InternalizeL(). Ниже представлен фрагмент этой функции:

aStream >> card;
const TInt count( card );
TRgb rgb;

for ( TInt ii=0; ii < count; ii++ )
{
aStream >> rgb;
iEikColors->AppendL( rgb );
}

Здесь мы можем убедиться, что для каждого встроенного класса вызывается оператор ">>". Если мы проследим все вызовы этих функций, то обнаружим, что они создают данные при помощи 32-битных блоков, и каждый такой блок отдельно считывается из файла. Даже если для нас у файлового сервера есть заранее считанные данные, работа функций все равно приведет к снижению производительности всей программы. Вместо вышеприведенного примера, попробуйте посмотреть на следующий пример кода:

aStream >> card;
const TInt count( card );
aStream->ReadL( iEikColors, count * sizeof( TRgb ) );

Такой код будет работать значительно быстрее, и для него нужно всего лишь убедиться, что внутренний формат структуры TRgb не был изменен.

Неэффективное использование базы данных

Проблемы неэффективного использования систем баз данных в Symbian OS очень близки по смыслу проблемам неэффективного использования файловой системы. Обе системы баз данных, имеющиеся в Symbian OS, весьма широко используют в своей работе файловую систему. Поэтому неправильное использование API баз данных может привести к тем же проблемам, что и в случаях неэффективного использования файловой системы.

Первое, что нужно отметить, - это использование Compact() API. В целях улучшения качества работы базы данных, их подсистемы не производят изменения над данными базы "на лету". Изменения базы данных происходит лишь по прошествии определенного отрезка времени или по накоплению определенного количества изменений в структурах базы. Во время изменения, новые структуры добавляются в конец базы данных, и помечаются различными маркерам. Постоянные изменения содержимого базы данных приводят к росту ее объема. Именно поэтому и существует Compact() API. При помощи Compact() API мы можем существенно перестроить базу данных, удалив из нее все неиспользуемые области. Совершенно очевидно, что такая операция будет весьма длительной для больших и сложных баз данных, поэтому ее следует проводить только когда это действительно нужно.

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

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

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

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

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

Плохое использование шаблонов проектирования

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

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

Рассмотрим следующий пример: в архитектуре было решено применить шаблон состояния (state pattern). Одним из способов реализации этого шаблона является наследование от общего базового класса специализированных классов для каждого состояния некоторого объекта. Для инициализации конечного автомата, разработчики выбрали фабричный шаблон (factory pattern):

CExampleStateFactory* CExampleStateFactory::NewL()
{
CExampleStateFactory* factory = new ( ELeave ) CExampleStateFactory();
CleanupStack::PushL( factory );

// Создать все состояния
factory->iStates[ EError ] = new ( ELeave ) TExampleStateError( *factory );
factory->iStates[ EStarted ] = new ( ELeave ) TExampleStateStarted( *factory );
factory->iStates[ EStopped ] = new ( ELeave ) TExampleStateStopped( *factory );

// И так далее...

CleanupStack::Pop();
return factory;
}

Фабрика состояний обладает всеми инициализированными объектами, каждый из которых содержит указатель на фабрику. Однако если мы более детально рассмотрим класс состояния:

class TExampleStateBase
{
public:
TExampleStateBase( CExampleStateFactory* aFactory );

inline TExampleStateBase* GetState( TStateEnum aState )
{
return iFactory->GetState( aState );
}

private:
CExampleStateFactory* iFactory;
}

TExampleStateBase::TExampleStateBase( CExampleStateFactory* aFactory )
: iFactory( aFactory )
{
}

TExampleStateStarted::TExampleStateStarted( CExampleStateFactory* aFactory )
: TExampleStateBase( aFactory )
{
}

Мы заметим, что указатель на фабрику, хранимый каждым из объектов состояний, используется только лишь в момент переключения состояния. Таким образом, благодаря шаблону состояний наш код весьма и весьма усложнился. Если объект фабрики будет очень интенсивно использоваться, это приведет к существенному снижению производительности. Так же весьма большое количество кода используется для инициализации фабрики, то в конечном итоге это приводит еще и к увеличению затрат на стороне ROM-памяти.

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

Если каждый из объектов состояний является простым, и не содержит внутри данных, тогда весь конечный автомат мог быть реализован в виде таблицы указателей на виртуальные функции. В этом случае совершенно отпала бы необходимость реализации фабричного шаблона, а состояния конечного автомата изменялись бы при помощи приведения типов. Хотя стоит отметить, что при этом на порядок бы уменьшилась читаемость кода, и значительно бы возрос риск скрытых дефектов.

Обычный код, и код, создаваемый "на будущее"

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

Представьте себе приложение, хранящее параметры конфигурации в файле ".ini". Во время разработки эти параметры очень часто меняются, поэтому их очень удобно хранить в форме, удобной для изменения. Однако по завершении разработок, эти параметры становятся постоянными, и теперь уже ресурсы и быстродействие приложения тратятся на считывание и разбор этих данных. В этом случае было бы неплохо применить какой-нибудь фреймворк или плагин от Symbian OS, созданный как раз для хранения параметров приложения, однако и в этом случае есть вероятность их неправильного использования.

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

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

Код, создаваемый "на будущее", так же может стать хорошей практикой, однако опять же, если имеющиеся инструменты будут использованы неправильно, это снова приведет к росту кода и потере производительности приложения.

Разработка и тестирование на эмуляторе

Эмулятор Symbian OS специально создавался как инструмент разработки, позволяющий немедленно перейти от написания кода к его исполнению. Эмулятор так же позволяет отладить код во время его разработки и исполнения, не используя при этом дорогих электронных прототипов.

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

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

Вы, ваш компилятор и ваш код

Знание особенностей компилятора так же важно, как и знание используемой вами операционной системы и языка программирования. Многие из современных компиляторов имеют целый ряд оптимизирующих процессов, пытающихся произвести для вас код желаемого качества, будь то самый компактный или самый быстрый код. И для вас будет очень полезно знать какой код компилятор пытается произвести. Так же очень важно помнить, что "фокусы" одного компилятора не всегда будут повторяться на других компиляторах.

Не идите против компилятора

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

Выучите немного ассемблера

Ассемблер многими воспринимается как черная магия, и отвергается большинством разработчиков. Однако для полного понимания того как будет работать ваш код на конкретной платформе, поверхностное понимание ассемблера будет очень и очень полезным.

Маленькие советы

Помимо "убийц производительности", в данном буклете так же представлены маленькие советы, которые помогут вам создавать более эффективный код. Многие из этих советов носят общий характер, и детально рассмотрены в стандартах кодирования для Symbian OS. Некоторые из них все же стоит упомянуть прямо здесь.

Избегайте вызова функций внутри определения условия циклов

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

Используйте ссылки или указатели там, где это нужно

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

Не раскручивайте циклы

Для современных компиляторов оптимизации в виде размотки или раскрутки циклов уже не нужны. Более того, такие оптимизации могут больше навредить, чем помочь. Позвольте компилятору самому решить что и как оптимизировать в вашем цикле.

Избегайте длинных блоков из if и else

Лучше всего воспользоваться блоком switch, так как он может более эффективно выполнить эту работу. Если условия перехода не являются постоянными целочисленными типами, как например строки, тогда попробуйте до блока switch воспользоваться таблицей поиска (lookup table) для вычисления таких констант.

Используйте соответствующим образом квалификатор const

Благодаря определению только считываемых переменных в виде const, вы позволяете компилятору сгенерировать боле эффективный код.

Быстрый анализ кода при помощи User::FastCounter()

Библиотека пользователя при помощи метода User::FastCounter() дает возможность извлечь системное время с достаточной точностью, чтобы измерить скорость выполнения того или иного кода. Реализация этого метода зависит от устройства, однако при помощи API HAL:Get() можно узнать параметры этого счетчика: параметр EFastCounterFrequency дает возможность узнать частоту счетчика, а параметр EFastCounterCountsUp - направление отсчета, используемого в счетчике.

Инструменты: пошаговый анализатор

Данная глава предназначена для тех разработчиков, кто имеет доступ к лицензионным прототипам, определенным уровням SDK, или использует эталонные платы.

Пошаговый анализатор может использоваться для получения грубой, статической картины работы кода в реальном устройстве. Все, что он делает, - это каждую миллисекунду сохраняет в файле-логе ID работающего потока, и текущее значение счетчика команд (program counter). Для анализа сохраненных данных используется специальная программа, работающая из командной строки компьютера. Полученная таким образом информация может помочь в обнаружении проблем с производительностью программы, и помочь в пересмотре кода на предмет "узких мест".

Компиляция ROM-файла

Для того, чтобы было можно использовать анализатор, его нужно включить в состав ROM-файла. Делается это при помощи указания файла "profiler.iby" в строке с командой "buildrom":

buildrom h4hrp techview profiler.iby


Запуск анализатора

Самый простой способ запустить анализатор - это вызвать его в командной строке eshell:

start profiler start

Команда запустит отдельный поток, в котором будет работать анализатор. Благодаря этому потоку вы сможете переключаться на другие задачи при помощи комбинации клавиш Ctrl+Alt+Shift+T.

Запуск изучаемого кода

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

Остановка анализатора

После того, как вы получите желаемые данные, переключитесь обратно в командную оболочку eshell, и остановите анализатор:

profiler stop

А затем, закройте и файл, использовавшийся для сохранения получаемых данных:

profiler unload


Получение данных анализатора

В корне диска C вашего устройства вы должны обнаружить файл "profiler.dat". Для передачи его на компьютер, переместите его на карточку памяти вашего устройства.

Анализ данных

Вначале вам следует конвертировать данные из файла "profiler.dat" в формат, понятный программе Excel, так как именно в ней вы сможете создать график активности вашего кода.

Копирование файла с данными

Скопируйте файл "profiler.dat" в ту директорию, где хранятся ваши ROM-файлы. Это делается для возможности использования таблиц символов, использовавшихся для создания ROM-файлов, так как именно при помощи этих таблиц можно извлечь собранные данные. Запустите следующую команду:

analyse -r h4hrp_001.techview.symbol profiler.dat -v -mx > profile.xls


Создание графа активности

Откройте файл "profile.xls" в Excel-е. Удалите первые 6 рядов из таблицы, чтобы ваш граф показывал имена потоков. В первых шести рядах хранятся суммарные данные, которые не стоит добавлять в граф. Подобным образом значения времени, сохраненные в первом столбце, будут лишними для графа, однако удалять их не стоит, так как они будут служить перекрестными ссылками для тех областей данных, которыми вы интересуетесь.

Выделите все данные, и запустите помощника для создания графов. В открывшемся окошке проделайте следующее:

  • В качестве типа графа выберите "Area", а в качестве подтипа - "Stacked". Нажмите "Next".

  • Выберите область данных так, чтобы не затрагивать первый столбик. Измените столбик "А" на "B", то есть =profile!$A$2:$V$941 должно измениться на =profile!$B$2:$V$941. Нажмите "Next".

  • Ничего не делайте в открывшемся окошке, и просто нажмите "Next". В конце выберите "As new sheet", и нажмите "Finish".



Выберите активную область и потоки

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

Создание листинга по всем функциям

Как только вы узнаете время выполнения каждого потока, вы так же сможете создать список функций, упорядоченный по их активности. Например, для того чтобы узнать какие функции вызывались между моментами 51300 и 76900 в потоке EFile, нужно запустить следующую команду:

analyse -r h4hrp_001.techview.symbol profiler.dat -bf -p -s51300-76900 -t EFile* > analysis.txt

При этом следует обратить на следующие особенности:

  • При определении значений параметра "-s", между заданными значениями моментов времени не должно быть никаких пробелов.

  • Моменты времени берутся из первого столбика файла "profile.xls".

  • После параметра "-t" сначала идет пробел, и уж потом - имя искомого потока. В названии потока можно использовать символы для поиска как в его начале, так и конце.


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

Ресурсы для разработчиков

Symbian Developer Network

Symbian Developer Network newsletter

Symbian OS Tools Providers

Sony Ericsson Developer World

Forum Nokia

Sun Microsystems Developer Services

11 авг. 2009 г.

Знания и опыт

После нескольких прочитанных книг о Symbian OS и S60, после всех переводов, в очередной раз поймал себя на одном и том же: полез в документацию по Symbian OS за разъяснением достаточно пустякового вопроса. Пустяковый вопрос касался использования стека очистки - темы, изъезженной вдоль и поперек многочисленной документацией, статьями, книгами и всевозможными буклетами. Отсюда напрашивается старый как мир вывод - сколь бы ни была хорошо освоена документация, статья или книга, человек все равно будет доверять только своему опыту (или набитым шишкам, если хотите).

Поэтому в деле освоения Symbian OS (и S60 в том числе), я настоятельно рекомендую набираться опыта и ума исключительно при помощи программирования, а не при помощи перечитывания гор книг, статей и буклетов. Конечно, без вводного материала новичку будет очень и очень трудно. Дело даже не в особенностях языка Symbian С++, а в особенности работы самой Symbian OS, претерпевшей за несколько десятилетий множество изменений, и впитавшей в себя множество весьма экзотических особенностей (как, например, использование того же стека очистки и дескрипторов). Однако лучший способ изучить язык программирования, и среду, в которой он применяется, - это создавать полноценные приложения для настоящих пользователей, а не гнаться за теоретическими знаниями в попытках приобрести реальный опыт.

6 авг. 2009 г.

Веб-клиент на Perl-е

Создание веб-клиента на Perl-е оказалось не такой уж и трудной задачей. В сети выложено множество разнообразных примеров, а библиотека LWP дает все необходимое, что может понадобиться в большинстве случаев. Причем, вместо изучения мануалов по сетевому программированию на Perl-е, был выбран сугубо практический подход: полная нацеленность на решение технической проблемы. Результат не заставил себя ждать: за три часа был создан как веб-клиент, так и его достаточно несложные "мозги". Из всего этого меня больше всего порадовали большие возможности Perl-а, и наличие несметного количества примеров в сети. Без примеров из сети у Perl-а не было бы никаких шансов продемонстрировать свои большие возможности. По крайней мере, на моем компьютере.

27 июл. 2009 г.

"Встраиваемые шаблоны", или пара слов о "thin templates"

В Symbian C++ есть такое понятие как "thin template". Многие разработчики дословно переводят термин "thin template" как "тонкий шаблон". На самом деле термин "thin template" следует переводить как "встраиваемый шаблон". Объясню почему.

Из описания "Thin templates" становится ясно, что шаблоны используются для тех же целей, что и в других языках программирования: для создания классов-коллекций, и обеспечения безопасности хранимых типов данных.

В то же самое время, использование шаблонов существенно увеличивает объем объектного кода, что является весьма нежелательным явлением в мобильных устройствах. Для снижения объемов объектного кода разработчики Symbian OS придумали идиому встраиваемых шаблонов.

Во-первых, для создания класса со встраиваемыми шаблонами нужен базовый класс, хранящий всю логику и код обработки данных. Базовый класс должен использовать параметры типа TAny, и по возможности содержать весь код обработки данных, так как наследуемые от него классы (как раз использующие шаблоны), будут выполнять лишь функцию внешних интерфейсов к полезному коду базового класса:

class CArrayFixBase ... { // объявление базового класса
...
IMPORT_C const TAny* At(TInt aIndex) const;
}

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

class CArrayFix : public CArrayFixBase {
...
inline const T& At(TInt aIndex) const
{
return (*((const T *) CArrayFixBase::At(anIndex)));
}

При этом следует обратить внимание на тот факт, что все методы наследующего класса должны быть встраиваемыми, то есть объявленными через оператор inline. Только в этом случае для наследующего класса не будет создан избыточный объектный код. Таким образом, шаблон "встраивается" в базовый код через наследующий класс, и не позволяет расти объектному коду.

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

8 июн. 2009 г.

Русские строки в UI

Так уж получилось, что на данный момент Symbian OS является властительницей смартфоновских дум практически на всем земном шаре, в том числе и в России. А так как смартфоны в первую очередь подразумевают наличие на борту полезных программ и утилит, то без локализации UI никак не обойтись.

Конечно, при особом желании, и наличии весьма сомнительного изобретательства, своих клиентов можно пичкать такими "шедеврами" локализации как "BbIxo/\", или "BBE/\|/|TE /\AHHbIE", или, не мудрствуя лукаво, вместе с программой раздавать английские словарики. Однако если есть желание сделать нормальный UI, то следует в первую очередь воспользоваться инструментами локализации самой Symbian OS.

Unicode

Юникод, как это хорошо известно, имеет под собою благородный порыв стать домом всех не-ASCII-тичных символов. Однако с форматом у юникода дела обстоят примерно так же, как и у его предка - ASCII. Юникод на данный момент, имеет 8-ми, 16-ти, и 32-х битную версии. Для особых эстетов найдется даже 7-битная версия юникода. Однако для возможности использования русских букв и слов в строках мобильного пользовательского интерфейса в Symbian OS, будет совершенно достаточно кодировки UTF-8.

Файл .loc

То, что хранить строковые константы в теле программы - вселенское зло, вам объяснит любой грамотный программист. Symbian C++ в этом плане - не исключение, поэтому все строки локализации, с таким содержимым как "Выход", "Введите данные" или "Федя Пупкин Production", выносятся в текстовый файл с расширением loc. По большому счету, совершенно неважно какое расширение вы дадите файлу. Оно может быть и lok, и loh, и abc. Главное - это знать, что в данном файле хранятся все ваши локализованные строки.

Внутри своего loc-файла вы должны разместить не только строки в виде определений #define, но так же указать компилятору ресурсов в какой кодировке сохранен ваши строки, а равно - и сам loc-файл.

Для указания кодировки используется макрос CHARACTER_SET. Официальных параметров у макроса - всего два:

CHARACTER_SET UTF8 // ваши строки, понятное дело, в кодировке UTF-8
// или
CHARACTER_SET CP1252 // ваши строки в кодировке Windows Cp-1252

Для примера предположим, что у нас есть файл "application.loc", содержимое которого будет следующим:

// application.loc
CHARACTER_SET UTF8 // теперь компилятор в курсе, что мы используем UTF-8

#define about_fedya "Федя Пупкин Production"

В результате мы получили loc-файл с правильным содержимым. Дотошности ради стоит сообщить, что UTF-8, о котором мы говорим, на самом деле не так прост как кажется. Иногда Symbian OS не совсем правильно понимает все нюансы формата UTF-8, и поэтому у многих программистов возникают проблемы как с компилятором ресурсов, так и с текстовым редактором.

Однако файлы в "правильной" кодировке UTF-8, "понятные" для компилятора ресурсов Symbian OS, на самом деле очень легко создаются при помощи столь ненавистного многим компьютерщикам текстового редактора Windows Notepad. Да, вы не ослышались - Windows Notepad. Правда при сохранении вашего текстового loc-файла следует не зыбыть выбрать кодировку "UTF-8". После этого ни один компилятор ресурсов не плюнется вам в лицо гневными сообщениями о неправильных кодировках.

Файл .rss

Как правило, стоковые константы, определенные в loc-файле, находят свое применение в файле ресурсов приложения, носящее столь звонкое сейчас расширение - rss. Рассмотрим пример такого rss-файла, в котором будут определяться все элементы UI нашего приложения:

#include "application.loc" // подскажем компилятору где находятся наши строки

...

// теперь строковыми константами можно воспользоваться прямо в меню
MENU_ITEM
{
command = EShowInfoAboutAuthor;
txt = about_fedya;
},

...

// или просто в виде ресурсной строки
RESOURCE TBUF r_about_fedya { buf = about_fedya; }

В принципе, уже на этом этапе диалоги и менюшки ваших программ должны заговорить на великом и могучем русском языке. Точно так же вы можете "украинизировать", "обелорусить" или даже "закиргизить" свое приложение, если того потребуют ваши пользователи. Никакой принципиальной разницы тут нет. Главное - содержимое вашего loc-файла и грамотное указание используемой кодировки.

Загружаем локализованную строку в программе

Как бы ни были удобны rss-файлы, порою нам нужно загрузить строку из файла ресурсов, и показать пользователю. Как это сделать в Symbian C++? На самом деле - проще пареной репы. Для приготовления такой "пареной репы" вам понадобится компонента под емким названием StringLoader. Эта замечательная утилита доступна для всех, кто работает на платформе S60. Впрочем, возможно и на других платформах есть подобные аналоги. Итак, чтобы загрузить нашу тестовую строку, следует выполнить следующий код:

// создать переменную для хранения строки
HBufC* aboutFedya;

// загрузить строку "r_about_fedya" из файла ресурсов
// обратите внимание, что тут мы должны исользовать только большие буквы
aboutFedya = StringLoader::LoadLC( R_ABOUT_FEDYA );

// сделать что-нибудь с полученной строкой

...

// освободить память, занятую строкой
CleanupStack::PopAndDestroy( aboutFedya );

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

19 мая 2009 г.

Enum

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

enum Values
{
Value 1 = 0,
Value 2 = Value 1,
Value 3 = Value 1,
Value 4,
Value 5
};

И теперь мне хочется сказать программисту, придумавшему подобное гениально решение: так держать! Если ты и дальше будешь так же старателен, умен и изобретателен, то обязательно когда-нибудь получишь Шнобелевскую премию.