std::chronoによる処理時間測定

処理時間の測定は、clockやgettimeofdayを使う事が多いと思いますが、あまりC++っぽくありません。そこで、C++0xからstd名前空間に入ったstd::chronoを使ってみようと思います。

std::chrono::system_clock::now()で現在の時刻(time_point)が取れるので、それを時間を計測したい処理の前後で取り、差を求めます。operator -が使えるのがC++っぽくていいですね。system_clock::now()で取れる時間の精度は実装依存なので、差を求める時にmillisecondsに変換して返しています。

#include <iostream>
#include <chrono>
#include <unistd.h>

template <class F>
std::chrono::milliseconds
take_time(F&& f) {
    namespace chrono = std::chrono;

    auto const begin = chrono::system_clock::now();
    f();
    auto const end = chrono::system_clock::now();
    return chrono::duration_cast<chrono::milliseconds>(end - begin);
}

int main() {
    auto const time = take_time([] {
        sleep(5);
    });
    std::cout << "time: " << time.count() << " ms" << std::endl;
    return 0;
}

Boost.GILでブレセンハムのアルゴリズム

Boost.GILで線を引きたくなったので作ってみた。

ある点 a から点 b まで線を引くパターンで、b.x >= a.x かつ b.y >= a.y かつ b.x - a.x >= b.y - a.yのパターンのみ実装しています。それに合わないケースはrotated_viewやtransposed_viewで視点を変更して、前途の条件に合わせるようにviewを作り替えています。

#include <iostream>
#include <cassert>
#include <cmath>
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_io.hpp>

template <class View, class T, class Value>
void bresenham(View& view, boost::gil::point2<T> const& a, boost::gil::point2<T> const& b, Value const& v) {
    assert(b.x >= a.x);
    assert(b.y >= a.y);
    assert(b.x - a.x >= b.y - a.y);

    T const dx = b.x - a.x;
    T const dy = b.y - a.y;
    T e = -dx;
    for (T x = a.x, y = a.y; x < b.x; x += 1) {
        view(x, y) = v;
        e += 2 * dy;
        if (e >= 0) {
            y += 1;
            e -= 2 * dx;
        }
    }
}

template <class View, class T, class Value>
void line(View& view, boost::gil::point2<T> const& a, boost::gil::point2<T> const& b, Value const& v) {
    namespace gil = boost::gil;
    typedef gil::point2<T> point_t;

    auto const dx = b.x - a.x;
    auto const dy = b.y - a.y;

    if (dx >= 0 && dy >= 0) {
        if (dx >= dy) {
            bresenham(view, a, b, v);
        } else {
            auto view2 = gil::transposed_view(view);
            bresenham(view2, point_t(a.y, a.x), point_t(b.y, b.x), v);
        }
    } else if (dx >= 0) {
        auto view2 = gil::rotated90ccw_view(view);
        line(view2, a, point_t(b.x, b.y - 2 * dy), v);
    } else if (dy >= 0) {
        auto view2 = gil::rotated90cw_view(view);
        line(view2, a, point_t(b.x - 2 * dx, b.y), v);
    } else {
        line(view, b, a, v);
    }
}

int main() {
    namespace gil = boost::gil;

    gil::point2<ptrdiff_t> size(300, 300);
    gil::rgb8_image_t img(size);
    auto view = gil::view(img);

    gil::fill_pixels(view, gil::rgb8_pixel_t(0, 0, 0));

    for (double theta = 0; theta < 2 * M_PI; theta += M_PI / 12.0) {
        int x = std::round(std::cos(theta) * 80 + 150);
        int y = std::round(std::sin(theta) * 80 + 150);
        line(view, gil::point2<int>(150, 150), gil::point2<int>(x, y), gil::rgb8_pixel_t(255, 255, 255));
    }

    gil::png_write_view("line.png", view);
}

Boost.GILによるpng生成

libpngってC++ friendlyじゃないよね、って呟いていたら、id:faith_and_braveさんに「そこでBoost.GILですよ」と言われたので、作ってみた。

#include <utility>
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_io.hpp>

int main() {
    namespace gil = boost::gil;

    gil::point2<ptrdiff_t> dim(400, 400);
    gil::gray8_image_t img(dim);
    auto view = gil::view(img);

    for (ptrdiff_t i = 0; i < dim.y; ++i) {
        for (ptrdiff_t j = 0; j < dim.x; ++j) {
            view(i, j) = 255 * (std::min(i, j) / static_cast<double>(dim.x));
        }
    }

    gil::png_write_view("img.png", view);

    return 0;
}

IO時にlibpngを読んでいるので、

 $ g++-4.5 -std=c++0x -o gil gil.cpp -W -Wall -O3 -lpng

のようにlibpngをリンクしないとコンパイルできません。

std::asyncによる並列for_each

C++ではOpenMPよりも、std algorithmが並列に動いた方が便利だよね。ということで作りました。元ネタはhttps://twitter.com/cpp_akira/status/16195866927です。

#ifdef _OPENMP
inline size_t concurrency_omp() {
    size_t n;
#pragma omp parallel
    { n = omp_get_num_threads(); }
    return n;
}
#endif

inline size_t concurrency() {
#ifdef _OPENMP
    static size_t const n{concurrency_omp()};
#else
    // g++ 4.5's hardware_concurrency() always returns zero :-(
    auto const n = std::thread::hardware_concurrency();
#endif
    return std::max<decltype(n)>(1, n);
}

template <class It, class F>
void for_each(It begin, It end, F&& f) {
    auto const total = std::distance(begin, end);
    auto const c = (size_t)total < concurrency() ? 1 : concurrency();
    auto const num = total / c;

    std::vector<std::future<void> > futures;
    futures.reserve(c);

    for (size_t i = 0; i < c; ++i) {
        futures.push_back(std::async([&, i] {
            std::for_each(begin + num * i, begin + num * (i + 1), f);
        }));
    }
    std::for_each(begin + num * c, end, f);
    std::for_each(futures.begin(), futures.end(), [] (std::future<void>& f) { f.wait(); });
}

割と適当です。Itはrandom access iteratorでないとエラーになります*1。g++のstd::thread::hardware_concurrencyは常に0が帰ってくるので、OpenMPが使える時はそっちで並列度を取ってます。static変数の初期化はMT-safeなので、concurrencyを同時に呼んでも大丈夫だよね…?

Mac環境だとのサポートがまだないので、結構不便です…。

*1:std::vector::iterator とか