31 października 2012

Vector vs. List

Na tegorocznej konferencji GovingNative 2012 Bjarne Stroustrup, przedstawił kilka ciekawostek dotyczących nowości w C++:
Jednym z punków była wyższość wektora na listą. Lista góruje nad wektorem, w takich operacjach jak wstawianie oraz usuwanie elementów, jednak proces wyszukania odpowiedniego elementu w kontenerze okazuje się być naprawdę wąskim gardłem. Wektor z kolei lepiej radzi sobie z wyszukiwaniem, gorzej natomiast z usuwaniem i wstawianiem. Dzięki współczesnym procesorem korzystającym z mechanizmu dedukcji skoku, wektor okazuje się zyskiwać ogromną przewagę. Mimo iż wykonuje znacznie więcej operacji w pamięci to będą one odbywały się w cache procesora, gdzie są naprawdę szybkie.

Opis testu.
Wygeneruj N losowych liczb i wstaw je tak by zachowana została posortowana kolejność np. 5 1 4 2:
  • 5
  • 1 5
  • 1 4 5
  • 1 2 4 5

Usuń elementy z losowej pozycji np. dla 1 2 0 0, będzie to:
  • 1 2 4 5
  • 1 4 5
  • 1 4
  • 4
Na początek kilka funkcji pomocniczych. Pierwsza łączy nasz generator z rozkładem jednorodnym
TRnd get_rnd(const int max)
{
    const int seed = 858446;
    TBaseGeneratorType generator(seed);
    boost::uniform_int<> uni_dist(0, max);
    TRnd rnd(generator, uni_dist);
    return rnd;
}
Pierwsza część testu - generyczna funkcja do wstawiania, zachowując posortowaną kolejność.
template <typename T>
void insert_sort(T& con, const int size, TRnd& rnd)
{
    for (int i = 0; i < size; ++i) {
        const int value = rnd();
        typename T::iterator it = std::find_if(con.begin(), con.end(),
                                               value < _1);
        con.insert(it, value);
    }
}
Druga część testu - usuwanie wartości z losowej pozycji.
template <typename T>
void erase_by_rand_pos(T& con, TRnd& rnd)
{
    while (not con.empty()) {
        const int pos = rnd() % con.size();
        typename T::iterator it = con.begin();
        std::advance(it, pos);
        con.erase(it);
    }
}
Ciało testu:
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>

#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::lambda;
using namespace boost::posix_time;

typedef boost::mt19937 TBaseGeneratorType;
typedef boost::variate_generator<TBaseGeneratorType, 
                                boost::uniform_int<> > TRnd;

int main(int argc, char **argv)
{
    if (argc < 2) {
        cout << "Missing size argument" << endl;
        return -1;
    }

    TRnd rnd = get_rnd(std::numeric_limits<int>::max());
    const int size = boost::lexical_cast<int>(argv[1]);

    const ptime time_start = microsec_clock::local_time();
    cout << "Start at: " << time_start << ", size = "<< size << endl;

//    vector<int> container;
    list<int> container;
    insert_sort(container, size, rnd);

    const ptime time_insert = microsec_clock::local_time();
    cout << "Inserted sort:       " << time_insert - time_start << endl;

    erase_by_rand_pos(container, rnd);

    const ptime time_remove = microsec_clock::local_time();
    cout << "Erase at random pos: " << time_remove - time_insert << endl;
    cout << "Whole process:       " << time_remove - time_start << endl;

    return 0;
}
Wyniki. Wszystkie testy były kompilowane za pomocą g++ (4.6.3) z flagą -O2. Bjarne musiał korzystać z bardziej wyrafinowanego algorytmu niż mój, bo w jego wersji cały proces dla 200000 elementów zajął ok. 500 sekund, w moim przypadku było to już 1116 sekund (sic!), dlatego też nie testowałem powyżej tej wartości. Nie testowałem też "preallocated list" bo nie jestem pewien co to dokładnie jest.



I jeszcze jeden wykres, przedstawiający jak czasowo wyglądały poszczególne operacje, czyli wstawianie z wyszukiwaniem i usuwanie z wyszukiwaniem. Porównując dane, widać, że w przypadku listy, najwolniejszą operacją jest usuwanie z wyszukiwaniem, w przypadku wektora wstawianie z wyszukiwaniem. Usuwanie i wstawianie na wektorze powinny być jego słabym punktem, ale okazują się, że w połączeniu z wyszukiwaniem, są znacznie szybsze od tego co oferuje lista.



29 października 2012

StrictMock i referencja w konstruktorze [TRICK]

Napotkałem ostatnio taki problem (mowa tu o testach charakteryzacyjnych).

Przykład: Chcemy przetestować metodę Filter::run(). Klasa do działania potrzebuje obiektów Inc i Show. Nie mają one czysto abstrakcyjnych interfejsów (ale zrobimy dla nich mocki). Show potrzebuje do działania referencji do Inc.

Problem pojawia się, gdy mock zrobimy StrictMock-iem i będziemy chcieli go jeszcze zainicjować. Tutaj ShowMock chcemy zainicjować IncMock (linijka 49-50).
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/ref.hpp>
#include <gmock/gmock.h>

using namespace std;
using namespace ::testing;

class Inc {
public:
    virtual int inc(int v) { return v++; }
};

class IncMock : public Inc {
public:
    MOCK_METHOD1(inc, int(int));
};

class Show {
    Inc& obj;
public:
    Show(Inc& i) : obj(i) {}
    virtual void show(int v) { cout << "Val: " << obj.inc(v) << endl; }
};

class ShowMock : public Show {
public:
    ShowMock(Inc& i) : Show(i) {}
    MOCK_METHOD1(show, void(int));
};

class Filter {
    boost::shared_ptr<Show> sw;
    boost::shared_ptr<Inc> in;
public:
    Filter(boost::shared_ptr<Show> s, 
           boost::shared_ptr<Inc> i) : sw(s), in(i) {}
    void run(int val) {
        if (val > 5)
            sw->show(val);
        in->inc(val);
    }
};

TEST(FilterTest, test) {
    boost::shared_ptr<IncMock> l_incMock(new StrictMock<IncMock>());

//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (*l_incMock));
//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (boost::cref(*l_incMock)));

    boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
                                             (boost::ref(*l_incMock)));
    Filter l_filter(l_showMock, l_incMock);

    int l_val = 8;
    EXPECT_CALL(*l_incMock, inc(l_val)).WillOnce(Return(l_val + 1));
    EXPECT_CALL(*l_showMock, show(l_val));

    l_filter.run(l_val);
}

int main(int argc, char *argv[]) {
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
Pierwsze rozwiązanie wypluje error przy kompilacji
In file included from /home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock.h:64:0,
                 from main.cpp:4:
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h: In constructor ‘testing::StrictMock<M>::StrictMock(const A1&) [with A1 = IncMock, MockClass = ShowMock]’:
main.cpp:48:77:   instantiated from here
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: error: no matching function for call to ‘ShowMock::ShowMock(const IncMock&)’
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: note: candidates are:
main.cpp:28:5: note: ShowMock::ShowMock(Inc&)
main.cpp:28:5: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘Inc&’
main.cpp:26:7: note: ShowMock::ShowMock(const ShowMock&)
main.cpp:26:7: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘const ShowMock&’
Co się okazuje, StrictMock ma tylko konstruktory explicit, dlatego podziała trick z boost::ref().
template <typename A1>
explicit StrictMock(const A1& a1) : MockClass(a1) {
  ::testing::Mock::FailUninterestingCalls(
      internal::ImplicitCast_<MockClass*>(this));
}
Niestety nie wiem dlaczego nie działa boost::cref() ?!

16 października 2012

Boost.Random

Boost.Random na szybko:
* Wybieramy jeden z generatorów pseudolosowych oferowanych przez bibliotekę (linia 9).
* Inicjujemy go seed-em (linia 12).
* Wybieramy rozkład zmiennej losowej (w naszym przypadku jednorodny), wybieramy też przedział, z którego będą wybierane liczby (obustronnie domknięty od 1 do 6) (linia 13).
* Łączymy generator z rozkładem w postaci naszego obiektu (kostka) (linia 16).
#include <iostream>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

int main()
{
    typedef boost::mt19937 TBaseGeneratorType;

    const int seed = 1337;
    TBaseGeneratorType generator(seed);
    boost::uniform_int<> uni_dist(1, 6);

    typedef boost::variate_generator<TBaseGeneratorType, boost::uniform_int<> > TVar;
    TVar die(generator, uni_dist);

    std::cout << "Na kostce wypadło: " << die() << std::endl;

    return 0;
}
Wynik:
Na kostce wypadło: 2

15 października 2012

Benchmark framework (Boost.Chrono) - część II

Druga biblioteka, z której pomocą chciałem mierzyć czas to Boost.Chrono. Nie do końca wyczuwam różnice między nią a Boost.Data_Time. Z pewnością obie mają rzeczy, których nie posiada "konkurent". Np. Boost.Chrono pozwala na zdefiniowanie z jakiego zegara systemowego będziemy wykorzystywać informacje. Ciekawe, czy zegar, który pokazuje czas wykonania danego wątku, uwzględnia, to że proces mógł być zawieszony, albo wywłaszczony na długi czas? Hmmm, jeszcze się w to nie wgłębiałem.

Pierwsza sprawa, to paczka w Ubuntu (libboost-all-dev), która ciągle wskazuje na wersję 1.46, a tam Boost.Chrono brakuje. Musiałem, wymienić to na:
apt-get install libboost1.48-all-dev
Druga spraw, to linkowanie. Chrono, korzysta z Boost.System i obie biblioteki trzeba zlinkować z programem (tu mam wątpliwości czy istnieje wersja, której nie trzeba linkować - nie doczytałem, korzystam z tego co mam w paczkach Ubuntu).

Zmiany w CMakeList.txt dla mojego projektu
project(benchmark_container_iterator)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})

find_package(Boost COMPONENTS system chrono REQUIRED)
target_link_libraries(${PROJECT_NAME}
  ${Boost_SYSTEM_LIBRARY}
  ${Boost_CHRONO_LIBRARY}
)
Dla benchmarka chciałem użyć process_cpu_clock, ale jego precyzja była do kitu. Jednak według opisu, wydaje się tym czego szukam:
Process and thread clocks are used usually to measure the time spent by code blocks, as a basic time-spent profiling of different blocks of code (Boost.Stopwatch is a clear example of this use).
Ostatecznie padło na "steady_clock"

Zasada działania, jest podobna jak w wersji poprzedniej. To bardzo proste wykorzystanie możliwości tej biblioteki.
#include <list>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>

#include <boost/chrono.hpp>

using namespace std;
using namespace boost::assign;

int main()
{
    using namespace boost::chrono;
    steady_clock::time_point time1 = steady_clock::now();
    cout << "Start at:              " << time1 << endl;

    const int size = 400500;
    list<int> l = list_of(1337).repeat(size, 1337);

    steady_clock::time_point time2 = steady_clock::now();
    nanoseconds td_init = duration_cast<nanoseconds>(time2 - time1);
    cout << "Memory Initialization: " << td_init << endl;

    int result = 0;
    BOOST_FOREACH(int value, l) {
        if (value > result)
            result = value;
    }

    steady_clock::time_point time3 = steady_clock::now();
    nanoseconds td_find = duration_cast<nanoseconds>(time3 - time2);
    cout << "Searching:             " << td_find << endl;

    return result;
}
Wyniki:
Start at:              14714172071593 nanoseconds since boot
Memory Initialization: 128273372 nanoseconds
Searching:             29473710 nanoseconds

14 października 2012

Benchmark framework (Boost.Date_Time)

Pisząc test porównujący czas wykonania dwóch algorytmów, natchnęło mnie by zrezygnować z wołania za pomocą metody system() polecenia wyświetlającego czas i skorzystać jakiejś biblioteki.
#include <iostream>
#include <stdlib.h>

int main()
{
    system("date +%M.%S.%N");
    return 0;
}
W boost znalazłem dwie, które nadawały by się do moich potrzeb. Niestety w żadną nie zagłębiałem się dokładnie. Pierwsza to Boost.Data_Time, druga to Boost.Chrono. Tutaj będzie tylko o pierwszej.

Biblioteka, pozwala na pobieranie czasu, obliczanie czasu trwania i przesunięć w czasie. Wszystko to dla różnych systemów kalendarzowych np. UTC, kalendarz gregoriański. Pozwala też zapisywać i odczytywać datę w różnych formatach - z tego co czytałem, bo nie miałem czasu testować.

W tym programie pobieram trzykrotnie lokalny czas (UTC) z dokładnością co do milisekund (linijki 14, 20 i 30). Następny krok to obliczenie czasu trwania (linijki 21 i 31) inicjalizowania listy, oraz jej przeszukiwania.
#include <list>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::assign;

int main()
{
    using namespace boost::posix_time;
    ptime time1 = microsec_clock::local_time();
    cout << "Start at:              " << time1 << endl;

    const int size = 400500;
    list<int> l = list_of(1337).repeat(size, 1337);

    ptime time2 = microsec_clock::local_time();
    time_duration td_init = time2 - time1;
    cout << "Memory Initialization: " << td_init << endl;

    int result = 0;
    BOOST_FOREACH(int value, l) {
        if (value > result)
            result = value;
    }

    ptime time3 = microsec_clock::local_time();
    time_duration td_find = time3 - time2;
    cout << "Searching:             " << td_find << endl;

    return result;
}
Wynik
Start at:              2012-Oct-14 17:13:36.716994
Memory Initialization: 00:00:00.125376
Searching:             00:00:00.021398

12 października 2012

SetArgReferee w DoAll

Czasami kod z którym mamy do czynienia, przekazuje nam wynik swojego działa zapisując go do argumentu przekazanego przez referencję. Chciało by się żeby wyglądało to troszkę schludniej, jednakże z powodów optymalizacyjnych (C++11 wprowadza coś takiego jak move constructor), trzeba z tym żyć, no i czasami testować. Google Mock wychodzi na przeciw, dostarczając m.in. metodę SetArgReferee() (jest też wersja dla wskaźników).

Poniżej będziemy testować klasę Simple, która posiada jedną metodą do zwracania pierwszego elementu wektora - returnFirst().

Przez "dependency injection", do klasy wstrzykiwany jest obiekt, który zadziała na wektorze (obróci go) nim zostanie zwrócony ten pierwszy (czyli ostatni element). Ten obiekt sobie z-mockujemy i tutaj przyda nam się SetArgReferee.
class IVecReverse {
public:
    virtual bool myReverse(std::vector<int>& v) const = 0;
};

class VecReverse : public IVecReverse {
public:
    bool myReverse(std::vector<int>& v) const {
        if (not v.empty()) {
            std::reverse(v.begin(), v.end());
            return true;
        }
        return false;
    }
};

class Simple {
public:
    Simple(IVecReverse& vs) : VecReverse(vs) {}

    int returnFirst(std::vector<int>& v) {
        if (VecReverse.myReverse(v))
            return v[0];
        return -1;
    }
protected:
    IVecReverse& VecReverse;
};
A teraz test. A więc z-mockowana metoda myReverse(), powinna przyjąć jakiś wektor (v1), który na końcu ma być odwrócony (to będzie nasze v2). Całą tą akcję trzeba zamknąć w DoAll(). W nawiasach <> dla SetArgReferee, wskazujemy numer parametru, dla którego robimy podmianę (mamy jeden argument więc będzie zero).
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

using namespace boost::assign;
using namespace ::testing;

class VecReverseMock : public IVecReverse {
public:
    MOCK_CONST_METHOD1(myReverse, bool (std::vector<int>&));
};

class SimpleTest : public Test {
public:
    VecReverseMock vrMock;
};

TEST_F(SimpleTest, testReturnFirst)
{
    Simple simple(vrMock);
    std::vector<int> v1 = boost::assign::list_of(3)(2)(4);
    std::vector<int> v2 = boost::assign::list_of(4)(2)(3);

    // Return musi byc ostatnie
    EXPECT_CALL(vrMock, myReverse(v1))
            .WillOnce(DoAll(SetArgReferee<0>(v2), Return(true)));
    EXPECT_EQ(4, simple.returnFirst(v1));
}

int main(int argc, char *argv[])
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
A teraz krótko o DoAll(a1, a2, ..., an) - jego zadaniem jest wykonanie wszystkich akcji od "a1" do "an", wszystkie poza "an" powinny zwracać void, "an" musi coś zwrócić bo taki będzie wynik całego DoAll (w naszym przypadku jest to Return(true)).
[----------] 1 test from SimpleTest
[ RUN      ] SimpleTest.testReturnFirst
[       OK ] SimpleTest.testReturnFirst (1 ms)
[----------] 1 test from SimpleTest (1 ms total)

9 października 2012

Boost.Assignment - list_of

Biblioteka, której celem jest łatwe wypełnianie kontenerów: http://www.boost.org/doc/libs/1_51_0/libs/assign/doc/index.html

Do wypełnienia większości kontenerów możemy posłużyć się metodą list_of(), w przypadku mapy będzie to metoda map_list_of(), a krotki tuple_list_of().
#include <vector>
#include <list>
#include <map>
#include <string>
#include <iostream>
#include <boost/foreach.hpp>

#include <boost/assign/list_of.hpp>

int main()
{
    std::vector<int> vec = boost::assign::list_of(45)(78);
    std::list<int> lst = boost::assign::list_of(105)(108);

    BOOST_FOREACH(int& value, vec) {
        std::cout << "Value: " << value << std::endl;
    }

    BOOST_FOREACH(int& value, lst) {
        std::cout << "Value: " << value << std::endl;
    }

    typedef std::map<std::string, int> map_type;
    map_type mp = boost::assign::map_list_of("first", 1)("second", 2);

    BOOST_FOREACH(const map_type::value_type& value, mp) {
        std::cout << "Value: " << value.first << std::endl;
    }

    return 0;
}
Wynik:
Value: 45
Value: 78

Value: 105
Value: 108

Value: first
Value: second
Biblioteka posiada dodatkowo szereg innych przydatnych funkcji: przeciążony operator +=, który umożliwia dopisywanie się na końcu wektora, insert() dostawianie dodatkowych elementów do mapy, albo repeat(), który kilkukrotnie powtarza wstawianie danej wartości, itp.
#include <boost/assign/std/vector.hpp> // dla'operator+=()'
#include <boost/assign/std/list.hpp>
#include <boost/assign/list_inserter.hpp> // dla 'insert()'

using namespace boost::assign; // dla wygody korzystania z +=

// ...

vec += 5, boost::assign::repeat(3, 69), 6;
lst += 8, 9;
insert(mp)("aaa", 3)("bbb", 4);
Wynik:
Value: 45
Value: 78
Value: 5
Value: 69
Value: 69
Value: 69
Value: 6

Value: 105
Value: 108
Value: 8
Value: 9

Value: aaa
Value: bbb
Value: first
Value: second