Монитор от Герба Саттера

В недавно вышедшем выступлении Герба Саттера посвященном многопоточности он привел интересный пример примитива для синхронного выполнения последовательностей операций. Сам Майерс окрестил детище “монитором”, по аналогии с мониторами из мира Java и C#, хотя на мой взгляд, сходства между ними не так уж и много. Суть задумки в том, что бы синхронизировать работу с каким-либо объектом, который изначально не поддерживает синхронизации. При этом, обеспечив синхронность не только на уровне одной операции, но и на уровне “трансзакции”.

string s = "Start\n";
vector<future<void>> v;

for(int i=0; i&lt;5; ++i)
{
    v.push_back(async([&,i]{
        {
            // необходимо сделать атомарно.
            s += to_string(i) + " " + to_string(i);
            s += "\n";
        }
        {
            // так же необходимо сделать атомарно.
            cout << s;
        }
    }));
}

В принципе, в приведенном выше примере вполне можно воспользоваться комбинацией mutex + lock_guard, но так не интересно, не красиво, да и о чем было бы рассказывать в течении полутора часов?
Решение предложенно действительно элегантное: Continue reading

Channel 9 продолжает радовать

Вообще, мне не очень нравится Channel 9. Обычно он представляет собой какое-то нелепое, скучное нагромождение мелкософт-ориентированной попсятины, что, вобщем-то ожидаемо, с учетом того кому этот канал принадлежит. Но, то ли в честь нового года, то ли в честь так и не пришедшего конца света, на сайте появилось еще одно замечательное выступление от Саттера: C++ and Beyond 2012: Herb Sutter – C++ Concurrency. Кстати, а до этого было не менее интересное C++ and Beyond 2012: Herb Sutter – You don’t know [blank] and [blank]

Ускорение ветвления в C++

Немного поигрался с довольно редко испольуземой при разработке приложений дерективой компилятора __builtin_expect. Эта директива поддерживатеся как Clang, так и в GCC, а в случае с MSVC есть довольно похожая директива __assume, работающая только для switch, и которую, если верить MSDN, использовать не рекомендутся, так как она ведет к потенциальным проблемам. Сказать насколько может быть полезна директива __builtin_expect сложно, но мало ли, к примеру товарищу на моем любимом форуме это зачем-то понадобилось.
Немного о самой __builtin_expect. При генерировании ассемблерных инструкций, компилятор самостоятельно решает о том, какая из ветвей условия должна быть размещена в начале, а к какой необходимо перейти посредством условного перехода. Данное поведение никак не регламентируется стандартом и полностью зависит не только от компилятора, но и от выбранного уровня оптимизации, в чем можно легко убедится получив несколько ассемблерных листингов одного и того же кода. При помощи же __builtin_expect можно указать компилятору какую из ветвей в условии if-else или switch-case разместить первой, а на какую организовать условный переход. Continue reading

Лучшее описание C++11

Существует довольно много статей посвященных C++11, которые в той или иной степени описывают вкусности из нового стандарта. Да что там говорить, на той же Википедии существует огромная, и вроде даже хорошая, статья посвященная C++11. Но, как известно, везде и всегда что-то не так…
Так вот, нашел я полностью устраивающее меня описание в виде презентации от Alex Sinyakov, за что ему огромное спасибо!

Тестовые задания в Яндексе. Часть 2.

Как и планировал, опробовал второй способ сортировки, а именно сортировку подсчетом. Кода в разы меньше, сам код куда проще, но… Крайне плохо масштабируемое решение с сильной зависимостью от количества доступной памяти. Так при использовании максимального количества доступной памяти в 256 Мб, приходится делать 64 прохода по файлу. Если же попытаться разнести чтение и запись (как я писал раньше, асинхронная запись дает ускорение приблизительно в 10-15%) то количество проходов вырастает до 128 и итоговая скорость оказывается даже меньше чем при последовательной обработке. Так же, мое решение не будет корректно работать в том случае, если количество одинаковых элементов превысит максимальное значение помещающееся в size_t.
Тем не менее, сортирует довольно быстро: 1 Гб, в среднем, обрабатывается за 108 секунд.

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

Тестовые задания в Яндексе

Когда-то, давным-давно, в разгар активного поиска работы я написал в Яндекс. Не то что бы я думал туда пройти, все же алгоритмы не моя сильная сторона, но мне подумалось “а почему бы не попробовать, особенно с учетом того, что на РСДНе ходят легенды о полнейшей невменяемости собеседующих там товарищей”. Вобщем решил сходить и чисто позырить. Позырить мне так и не удалось, т.к. яндексовцы дали тестовое задание на дом, а на такое я принципиально не соглашаюсь. Но, надо признать, задание было интересное, и я его прикопал с целью когда-нибудь, когда будет соответствующее настроение, решить. Соответствующего настроения не было у меня два года, и вдруг оно появилось! Continue reading

Поддержка C++11 в Clang

Зашел на страницу “C++11 implementation status” на сайте Clang и обрадовался. В Clang версии 3.1 появятся:

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

Сборка и тестирование проекта при помощи CMake и CTest.

Думаю что большинство C++ разработчиков так или иначе сталкивались с CMake, в то же время что такое CTest и как с его помощью можно автоматизировать модельное тестирование знают далеко не все.
Для того, что бы показать как можно использовать CMake и CTest вместе, я в качестве примера создал маленькую библиотечку, состоящую из пары строк кода, которое умеет складывать и делить Кроме приложений CMake и CTest, понадобится какая-либо UnitTest библиотека (в данном примере я взял Boost.Test, хотя, можно взять и GoogleTest). Тут стоит обратить внимание на то, что CTest не является фрэймворком для написания тестов. CTest – это приложение для запуска тестов и, если это необходимо, отправки результатов в какое-либо из поддерживаемых хранилищ. Continue reading

Когнитивный диссонанс

Вчера я был поражен на столько, что усомнился в том, во что верил уже лет 10. А именно в том, что при выравнивании 8, размер структур с полями {u64, u32, u16} и {u8, u32, u16} будет одинаков, т.е. равен 16. А ведь приложение собранное Rust утверждает что в первом случае размер 16, что и следовало ожидать, а во втором 12(!!!).

type tuple_type1 = (u64, u32, u16);
type tuple_type2 = (u8, u32, u16);

fn main() {
    io::println(#fmt("size of tuple_type1 = %u, size of tuple_type2 = %u",
        sys::size_of::<tuple_type1>(), sys::size_of::<tuple_type2>()));
    io::println(#fmt("align of tuple_type1 = %u, align of tuple_type2 = %u",
        sys::align_of::<tuple_type1>(), sys::align_of::<tuple_type2>()));
}

Содержимое консоли:

> ./main
size of tuple_type1 = 16, size of tuple_type2 = 12
align of tuple_type1 = 8, align of tuple_type2 = 8

Continue reading

using namespace std

В кругах разработчиков C++ периодически возникает спор на тему “почему нельзя/нужно использовать using namespace std“. Наткнулся на довольно показательный пример того, почему этого делать ни в коем случае нельзя.
Товарищ пытается написать удаление пробелов из строки:

#include <iostream>

#include <string>

using namespace std;

int main(int argc, char* argv[]) {
    string str = "";
    cin >> str;
    remove(str.begin(), str.end(), ' ');
    cout << str;
    cin.ignore();
}

и получет довольно неожиданную ошибку компиляции.

'remove': function does not take 3 arguments (C2660)

А все почему? Да потому-что никогда, ни при каких условиях, не надо давать символам из пространства std глобальную видимость! Ведь в данном случае, можно было получить куда более понятное “error: ‘remove’ was not declared in this scope” просто убрав using namespace std.