7 сентября 2009 г.

Отключение спикера на Ubuntu

Оказывается всё довольно просто. Чтобы отключить PC speaker на Ubuntu, нужно открыть файл /etc/modprobe.d/blacklist и добавить в него строку:

blacklist pcspkr

После этого нужно перезагрузиться.

Источник: ubuntuforums.org

1 сентября 2009 г.

Цитата: Хайнлайн

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

Эта цитата из Хайнлайна расползлась по инету в таком количестве, что первые несколько страниц Яндекса — это ссылки на блоги. Цитата настолько популярна, что, говорят, некоторые даже вставляют эту цитату в свое резюме.

Интересно, что народ ссылается на "своего любимого Хайнлайна", но при этом многие не имеют представления из какого произведения эта цитата и по какому поводу она вообще. Еще интересно, что именно в таком переводе эта цитата присутствует в книге "Прометей восставший" Роберта Уилсона (который имхо довольно некорректно ссылается на Хайнлайна), что, видимо, может говорить о том, что люди, не особо знакомые с источником, цитируют текст, вырванный другим человеком из контекста.

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

Фразу: «Я (мы, ты) просто должен…» — следует понимать в том смысле, что делать этого не нужно. Слова: «Это понятно каждому» — предупреждают о возможных сложностях. Если слышишь: «Конечно» — лучше проверить все самому. Эти маленькие клише и прочие им подобные, если уметь правильно читать их, позволяют хорошо ориентироваться.

Роберт Хайнлайн,
"Достаточно времени для любви,
или Жизни Лазаруса Лонга"

24 июня 2009 г.

Цитата: ДеМарко

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

Том ДеМарко, "Deadline. Роман об управлении проектами"

17 мая 2009 г.

Задачи на собеседованиях: односвязные списки

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

Дано: односвязный список N1->N2->N3->...->Nn и указатель на его голову N1.

Нужно: развернуть список за один проход так, чтобы стало Nn->Nn-1->Nn-2->...->N1.

5 мая 2009 г.

Цитата: Павич

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

Милорад Павич, "Хазарский словарь"

30 апреля 2009 г.

Кстати о GCC

Неделю назад вышла очередная версия компилятора GCC — 4.4.

В новой версии расширены возможности оптимизации кода, ключ -Wconversion теперь выводит предупреждение при некорректных попытках привести целочисленный тип к enum, а также включена экспериментальная поддержка стандарта C++0x. И много чего другого не менее интересного...

suncc в качестве подручного инструмента

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

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

Я хочу рассказать, как использовать компилятор Sun для того, чтобы повысить производительность вашего кода. У этого компилятора есть замечательная особенность — при выполнении оптимизации он может вставлять свои комментарии в объектные файлы, которые можно потом использовать для дальнейшего улучшения кода. Компилятор Sun прилагается к пакету Sun Studio, который является бесплатным и доступен для скачивания с http://developers.sun.com

В качестве примера рассмотрим простую программу:

int main ()
{
  int c = 0;
  int size = 10000;
  int *a = new int[size * size];

  for (int i = 0; i < size; ++i)
  {
    for (int j = 0; j < size; ++j)
    {
      a[j * size + i] = c++;
    }
  }

  delete [] a;
  return 0;
}

Эта программа создает квадратную матрицу размером size на size, и заполняет ее числами от 0 до N по столбцам. Для матрицы 10 на 10 это будет выглядеть так:

0 10 20 30 40 50 60 70 80 90
1 11 21 31 41 51 61 71 81 91
2 12 22 32 42 52 62 72 82 92
3 13 23 33 43 53 63 73 83 93
4 14 24 34 44 54 64 74 84 94
5 15 25 35 45 55 65 75 85 95
6 16 26 36 46 56 66 76 86 96
7 17 27 37 47 57 67 77 87 97
8 18 28 38 48 58 68 78 88 98
9 19 29 39 49 59 69 79 89 99

Проход по столбцам матрицы заведомо неэффективен, поэтому на такой программе должны быть хорошо видны результаты оптимизации. Попробуем замерить усредненное время выполнения программы для size=10000, скомпилированной с помощью g++ и sunCC:

g++ без оптимизации (g++ -O0 a.cpp): 2.6 сек
g++ c оптимизацией (g++ -O2 a.cpp): 2.0 сек

sunCC без оптимизации (sunCC a.cpp): 3.2 сек
sunCC c оптимизацией (sunCC -fast a.cpp): 0.4 сек


Опция -fast в suncc анализирует окружение и подбирает набор опций компилятора для максимального быстродействия. Видно, что sunCC в режиме оптимизации дает результаты в разы лучше, чем g++. А именно — в 5 раз. Хм...

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


Давайте посмотрим, что же делает sunCC при оптимизации. Для этого необходимо использовать при компиляции опцию -g0. Эта опция сохраняет в объектном файле отладочную информацию (в том числе и комментарии компилятора), но не запрещает инлайнить функции. Для просмотра комментариев нужно использовать программу er_src, которая также поставляется с Sun Studio.

$ sunCC -g0 -fast a.cpp
$ er_src a.o

Source file: ./a.cpp
Object file: ./a.o
Load Object: ./a.o

1. int main ()

2. {
3. int c = 0;
4. int size = 10000;
5. int *a = new int[size * size];
6.

Source loop below has tag L1
Induction variable substitution performed on L1
L1 interchanged with L2


7. for (int i = 0; i < size; ++i)
8. {

Source loop below has tag L2
Induction variable substitution performed on L2
L2 interchanged with L1


9. for (int j = 0; j < size; ++j)
10. {
11. a[j * size + i] = c++;
12. }
13. }
14.
15. delete [] a;
16. return 0;
17. }

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

Подсказка "Induction variable substitution performed" говорит нам о том, что компилятор обнаружил индукционную переменную и произвел ее замену. Это значит, что значение какой-то переменной может быть вычислено через значения итераторов циклов. После недолгого изучения кода заменяем строку "a[j * size + i] = c++;" строкой "a[j * size + i] = i * size + j;". И прогоняем компиляцию еще раз.

Видим, что сообщение об индукционной переменной пропало, но осталось другое сообщение: "L1 interchanged with L2". Это сообщение говорит нам о том, что компилятор поменял циклы местами, то есть заменил проход по столбцам проходом по строкам матрицы. Меняем циклы местами и снова прогоняем компиляцию. И убеждаемся, что сообщения о том, что компилятор что-то оптимизировал, пропали. То есть, оптимизировать больше нечего.

После наших изменений программа приняла вот такой вид:

int main ()
{
  int c = 0;
  int size = 10000;
  int *a = new int[size * size];

  for (int j = 0; j < size; ++j)
  {
    for (int i = 0; i < size; ++i)
    {
      a[j * size + i] = i * size + j;
    }
  }

  delete [] a;
  return 0;
}

Давайте теперь замерим время выполнения программы после всех наших изменений:

g++ без оптимизации (g++ -O0 a.cpp): 1.0 сек
g++ c оптимизацией (g++ -O2 a.cpp): 0.4 сек

sunCC без оптимизации (sunCC a.cpp): 0.6 сек
sunCC c оптимизацией (sunCC -fast a.cpp): 0.4 сек


На мой взгляд, получилось просто прекрасно! :)

Подробнее почитать о комментариях компилятора Sun можно здесь: http://docs.sun.com/app/docs/doc/819-5264/afapn?a=view

16 апреля 2009 г.

Задачи на собеседованиях: синхронизация

Предположим, что из доступных средств синхронизации у нас есть только бинарные семафоры (мьютексы). При этом семафоры поддерживают только операции lock() и unlock().

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

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

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

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

Подсказка:
Другими словами нужно самостоятельно реализовать аналог pthread_rwlock. Поэтому для начала нужно определить методы доступа к механизму синхронизации и отталкиваться уже от них. Например, пусть для потоков записи это будут функции writerLock() и writerUnlock(); для потоков чтения - readerLock() и readerUnlock(). Смысл этих функций аналогичен функциям lock() и unlock() для семафоров.

3 апреля 2009 г.

Кодоформатная паранойя

Есть у меня история из древности :)

Давным-давно программисты на Си при объявлении и определении функций не ставили пробелы между именем функции и открывающей круглой скобкой, а при вызове функций — ставили.

Для чего? Для того, чтобы grep'ом можно было легко найти или все вызовы функции или ее объявления.

Почему именно так ставили пробелы, а не наоборот? Потому, что иначе с define'ами была бы проблема. Ведь не напишешь же — "#define FUNC (A) ...". Только так — "#define FUNC(A) ...".

Возможно, некоторые моменты в этой истории спорные, но в общем история кажется мне вполне разумной.

1 апреля 2009 г.

Предупреждения GCC

Как я уже писал, компилятор gcc-4.3 умеет сообщать о потенциальной потере данных при преобразовании типов.

В дополнение к этому, в версии 4.3 появилось предупреждение о том, что объявление вида 'char *str = "abcd";' устарело и больше не должно использоваться. Причем предупреждение появляется даже без флагов -Wall, -Wextra и прочих. Как известно, использование в программе таких объявлений может привести к непредсказуемым последствиям. Вместо этого нужно использовать 'const char *str = "abcd";'. И это правильно.

UPD: Несмотря на дату, это не шутка :)