вторник, 18 ноября 2014 г.

Определение по списку в C++

В php есть возможность присвоить переменным значения используя массив.
list($a, $b) = array('str1', 'str2');
В pyton, вроде есть что то вроде этого:
a, b = ['str1', 'str2'];
А вот в C++ такой языковой конструкции нет. Но это совершенно не проблема, потому что ее можно сделать самому. Вот что у меня получилось

/**
 * This is a helper class.
 * It can be used only inside the function ListInitializer tolist(Args&& ...args)
 * ListInitializer list(Args&& ...args)
 * In all other cases, use it not possible
 * size is the number of arguments with which the constructor was called
 */
 
template<typename DataType, unsigned size>
class ListInitializer
{
public:
 
    /**
      * Assignment operator
      * Ref-qualified forbids such things:
      * ListInitializer c(a,b);
      * c = arr;
      * You can only use this form:
      * ListInitializer(a,b) = arr;
      * VecType must be convertable to DataType
      */

      template<typename ContainerType>
      ListInitializer &operator=(ContainerType &&arr) && // <-- Ref-qualified
      {
          auto it = arr.begin(), itend = arr.end();
          unsigned i = 0;
          while(it != itend && i < size) {
              *parr[i] = *it;
              ++it;
              ++i;
          }
          return *this;
      }
 
    /**
      * Deleted constructors. Forbids such things:
      * tolist q(a,b,c,d,e),w(a,b,c,d,e);
      * w = q;
      * You can only use this form:
      * tolist(a,b,c,d,e) = arr;
      */

    ListInitializer(const ListInitializer &that) = delete;
    ListInitializer() = delete;
 
 
private:
    /**
      * Constructor with one argument
      */

    explicit ListInitializer(DataType& arg) //: size(1)
    {
        helper(0, arg);
    }
 
    /**
      * Constructor with a variable (>1) number of arguments
      * You can use any number of arguments of type DataType in the constructor.
      * tolist(a,b,c,d) = arr;
      * tolist(a,b) = arr;
      */

    template <typename ...Args>
    ListInitializer(DataType& arg0, Args&... args) //: size(sizeof...(Args)+1)
    {
        helper(0, arg0, args...);
    }
 
    /**
      * Move constructor
      */

    ListInitializer(ListInitializer &&that)
    {
        for(unsigned i = 0; i < size; i++) {
            parr[i] = that.parr[i];
            that.parr[i] = nullptr;
        }
    }
 
    /**
      * Helper method.
      * Allows to initialize the list of any number of arguments.
      * Alternately, one by one makes pointers to the arguments into the internal array.
      */

    template <typename ...Args>
    void helper(int ind, DataType& arg0, Args&... args)
    {
        helper(ind, arg0);
        helper(++ind, args...);
    }
 
    /**
      * Helper method.
      */

    void helper(int ind, DataType& arg0)
    {
        parr[ind] = &arg0;
    }
 
    template <typename T, typename ...Args>
    friend ListInitializer<T, sizeof...(Args)+1> tolist(T& arg0, Args&... args);
 
    // Internal array of pointers to pointers to arguments
    DataType* parr[size];
 
};
 
 
template <typename DataType, typename ...Args>
ListInitializer<DataType, sizeof...(Args)+1> tolist(DataType& arg0, Args&... args)
{
    return ListInitializer<DataType, sizeof...(Args)+1>(arg0, args...);
}
 
 
// Check
#include <iostream>
#include <vector>
int main(){
 
    std::vector<std::string> arr{"str1","str2","str3","str4","str5","str6"};
    std::string a,b,c,d,e;
 
    tolist(b) = arr;
    std::cout << std::endl << a << " " << b << " " << c << " " << d << " " << e << std::endl;
 
    tolist(a,b,c,d,e) = arr;
    std::cout << std::endl << a << " " << b << " " << c << " " << d << " " << e << std::endl;
 
    return 0;
};
 
/*
 * g++ tolist.cpp -std=c++11 && ./a.out
 * or 
 * clang++ tolist.cpp -std=c++11 && ./a.out
 *
 * |output:
 * | str1   
 * |
 * |str1 str2 str3 str4 str5
 *
 *
*/

Нужен C++11 т.к. используется Ref-qualifiers PS blogger.com как всегда портит код. Надо менять блогодвижок.

среда, 20 августа 2014 г.

Микро-бенчмарк RocksDB server

В полноценном смысле то что я тут хочу написать, конечно бенчмарком не является. Но вполне способно дать понимание области применения RocksDB.

RocksDB -- это довольно таки крутое хранилище, являющееся (на данный момент) встраиваемым решением.
Главной фишкой RocksDB является то, что она рассчитана на использование на flash накопителях. Т.е. на SSD дисках.

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

Как я уже говорил, RocksDB -- встраиваемое решение. И не является сервером. Я честно ждал почти год, когда кто нибудь напишет или хотя бы начнет писать серверную обертку над ней. Но то что появилось за это время по разным причинам категорически меня не устраивает.
Поэтому я написал собственную серверную обертку: https://github.com/valmat/RocksServer. В настоящее время вполне функционален, протестирован и готов к работе. Но есть еще моменты требующие улучшения. Эти моменты ни как не связаны с его пригодностью к использованию, поэтому ничто не мешает уже сейчас произвести замеры производительности.

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

Итак, табличка.

MultiGet со случайными ключами.

Условия проведения эксперимента:
В бд 1000000 ключей. Выбирается набор случайных ключей в случайном порядке.
Кэш не используется. Прямо перед экспериментом я перезагрузил компьютер что бы полностью исключить возможность использования файлового кэша.
Хранимые значения имеют длину 50 +-5 байт
Количество
ключей в выборке
SDDHDD
504.7 ms131.3 ms
30012.6 ms2371.4 ms (~2s)
100029.5 ms7918.4 ms (~8s)
10 000124.6 ms45229.5 ms (~45s)
100 0002346.6 ms51855.8 ms (~56s)


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

Какие выводы можно сделать из таблички? RocksDB действительно очень быстрое хранилище и подходит для использования ее на SDD дисках.

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


суббота, 31 мая 2014 г.

Сlang vs gcc performance

Стало мне интересно и решил я провести такую глупую проверку: сравнить производительность программ откомпилированных Clang'ом и gcc

Для эксперимента взял первую попавшуюся реализацию пузырьковой сортировки с GitHub'а

Компилировал с опциями -g, -O1, -O2, -O3 и без опций

Получилось интересно и неожиданно.

Для запуска тестов использовал такой скрипт:
#!/bin/bash

OPT="-O3"
g++ $OPT bubble.cpp -o bubble1
clang++ $OPT bubble.cpp -o bubble2 #-stdlib=libstdc++
clang++ $OPT -stdlib=libc++ bubble.cpp -o bubble3 #-stdlib=libc++

#ls -slh
#exit;

sleep 3
time ./bubble1 > /dev/null
sleep 3
time ./bubble2 > /dev/null
sleep 3
time ./bubble3 > /dev/null
Меняя параметр OPT.
Поясню, что здесь компилируется.

g++ $OPT bubble.cpp -o bubble1
Используется gcc

clang++ $OPT bubble.cpp -o bubble2 #-stdlib=libstdc++

Используется clang со стандартной библиотекой libstdc++ от gcc

clang++ $OPT -stdlib=libc++ bubble.cpp -o bubble3 #-stdlib=libc++
Используется clang со своей собственной стандартной библиотекой libc++


Опция gcc clang -stdlib=libstdc++ (gcc) clang -stdlib=libc++
-g real 0m3.337s
user 0m3.335s
sys 0m0.004s
real 0m3.294s
user 0m3.296s
sys 0m0.000s
real 0m3.323s
user 0m3.325s
sys 0m0.000s
без опций real 0m3.334s
user 0m3.336s
sys 0m0.000s
real 0m3.293s
user 0m3.295s
sys 0m0.000s
real 0m3.320s
user 0m3.318s
sys 0m0.004s
-O1 real 0m1.735s
user 0m1.735s
sys 0m0.000s
real 0m1.485s
user 0m1.486s
sys 0m0.000s
real 0m1.493s
user 0m1.494s
sys 0m0.000s
-O2 real 0m1.516s
user 0m1.517s
sys 0m0.000s
real 0m1.522s
user 0m1.523s
sys 0m0.000s
real    0m1.495s
user 0m1.492s
sys 0m0.004s
-O3 real 0m1.510s
user 0m1.506s
sys 0m0.004s
real 0m1.534s
user 0m1.535s
sys 0m0.000s
real 0m1.498s
user 0m1.499s
sys 0m0.000s

Размеры бинарников:
Опция gcc clang -stdlib=libstdc++ (gcc) clang -stdlib=libc++
-g 22K 28K 65K
без опций 9,2K
8,3K 15K
-O1 8,9K 8,4K 12K
-O2 8,9K 8,1K 12K
-O3 8,9K 8,1K 12K

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

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

Мои версии clang и gcc:

gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)
Debian clang version 3.2-7ubuntu1 (tags/RELEASE_32/final) (based on LLVM 3.2)

четверг, 20 марта 2014 г.

отчетность в налоговую на Linux

Как я готовлю отчетность в налоговую.
Выписки у меня достаются в таком формате:
2014.01.20.rtf
2014.01.20-1.rtf
...
В первую очередь, нужно упорядочить по дате, поэтому переименовываем:
for i in `find . -type f -name "*.rtf*"`; do dst=`echo $i | sed -e :a -e 's/\(.*\)\([0-9]\{2\}\)\.\([0-9]\{2\}\)\.\([0-9]\{4\}\)\(.*\)/\1\4.\3.\2\5/;ta'`; echo mv $i $dst; done

Потом конвертируем в pdf
libreoffice --invisible  --convert-to pdf *.rtf
И соединяем все в один файл

gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=toprint.pdf -dBATCH `find . -type f -name "*.pdf" | sort`

Все.

понедельник, 10 марта 2014 г.

Документация PHP-CPP

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




PS вместо того, что бы что то писать здесь про библиотеку, я просто взял и начал переводить документацию на русский язык: http://phpcpp.ru/

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

воскресенье, 12 января 2014 г.

Сравнение производительности C++ php-расширения php с нативным кодом

Давно меня интересовал вопрос насколько добавляет производительности переписывание нативного кода в php-расширение.

И вот я решил провести сравнение.
В качестве платформы для написания расширения была выбрана библиотека PHP-CPP.

Пару слов о самой библиотеке PHP-CPP.
Довольно неплохая библиотека. Работать с ней приятно и удобно. Честно говоря, никогда еще не было так легко писать расширения для php.
Из минусов могу отметить недостаток документации. Что бы разобраться с некоторыми вещами недостаточно даже примеров, которыми автор снабжает код - приходится смотреть исходники. В частности я так и не разобрался, как перенести статический метод класса из C++ в  статический же метод в php.
Основана она на C++11 т.е. на старых дистрибутивах может потребоваться обновить gcc.

В качестве подопытного был выбран модуль постраничного разбиения.
Исходники на Гитхабе: https://github.com/valmat/myscrnav
Я написал два идентичных класса. Один на php, другой на C++ (в виде расширения к php).

Пару слов как пользоваться.
Пример использования есть в исходниках: https://github.com/valmat/myscrnav/blob/master/screennav_test.php

Создаем pagination-объект из расширения:
$scr = new myScrNav($pageNom, $Count, '/url/to/page/');
Или из php класса:
require 'php/class.screennav.php';
$scr = new ScreenNav($pageNom, $Count, '/url/to/page/');

Задаем необходимые параметры (если необходимо, можно не задавать):
    $scr->setInterval(10); // Сколько объектов на странице
    $scr->setPrefix('?qwe&part='); // URL prefix
    $scr->setPostfix('&prm=132'); // URL prefix
    $scr->setSpace('...'); // Разделитель блоков табов
    $scr->setCssName('newClassName'); // Имя класса css блока управления постраничным выводом
    $scr->setMidTab(15); // см. info.png
    $scr->setMaxTab(5); // см. info.png
    $scr->showCount(true); // Показывать ли общее количество элементов

Кроме того доступны следующие методы для получения вычисленных данных:
getStartPos() // Номер начального элемента на текущей странице (для выборки из БД)
getLimitPos() // Длина списка элементов на странице на текущей странице (для выборки из БД)
getPageCnt() // Количество страниц при разбивке на части
getStartPos() // Номер (вычисленный) текущей страницы
show()         // Возвращает собственно сам элемент управления постраничной разбивкой (html)
Т.е. можно управлять постраничной разбивкой и делать запросы к БД на основе этой постраничной разбивки. Внешний вид, разумеется, полностью настраивается через css

Теперь сами результаты сравнения.
PHP тестируется со включенным опкешером (apc). Без него смысла тестировать не вижу, ибо тестировать нужно так, как используется на рабочей системе.
1
Во-первых сравним просто функции.
На php будет
function ScreenNav_pageNo($var) {return (isset($_GET[$var]))?((int)$_GET[$var]-1):0;}
На C++
Php::Value GETpageNom(Php::Parameters &params) {
    if (params.size() == 0) return 0;
    string var   = (new Php::Value(params[0]))->stringValue();
    string get   = Php::globals["_GET"][var];
    long int rez = (new Php::Value( get ))->numericValue();
    return rez ? (rez-1) : 0;
}
php
memory usage: 0.56Kb
memory peak_usage: 1.1Kb
Вычесленное: time: 50•10-6 sec
ab -n 10000 ... : Time per request:       0.39 [ms] (mean)
C++
memory usage: 1.26Kb
memory peak_usage: 1.8Kb
Вычесленное: time: 60•10-6 sec
ab -n 10000 ... : Time per request:       0.4 [ms] (mean)

Как видно, на таком простом примере расширение не выигрывает, а даже проигрывает нативному коду.

2
Теперь сравним классы.

php
time: 215•10-6 sec
memory usage: 16.5Kb
memory peak_usage: 22.7Kb
 ab -n 10000 ... : Time per request:       0.533 [ms] (mean)
C++
time: 170•10-6 sec
memory usage: 2.3Kb
memory peak_usage: 4.6Kb
 ab -n 10000 ... : Time per request:       0.49 [ms] (mean)

Выводы.
Разница во времени порядка 10-5 sec - не то ради чего нужно переписывать с php на C++.
С другой стороны, само то, что она проявилась на такой незначительной задаче уже результат.
Приведенный пример позволяет понять порядок выигрыша и оценить целесообразность переписывания нативного кода в C++ расширение.
Обращает на себя внимание разница в потреблении памяти. Причем, если расширение выгрузить, то потребление памяти нативным кодом не уменьшается.