Thursday, August 28, 2008

IPv6 не будет

Слегка адаптированная мной заметка по мотивам http://www.extremetech.com/article2/0,2845,2328258,00.asp:

Arbor Networks измерили объем IPv6 траффика, проходящего через 2400 бэкбонов и роутеров у 87 провайдеров Интернет по всему миру. Учет велся с Июня 2007 по Июль 2008 года. Полученные данные были использованы для исследования, которое компания считает наиболее всеохватывающим исследованием ситуации с IPv6 на сегодняшнее время. В целом Arbor отследил 15 экзабайт траффика - при этом все человеческое знание занимает 4 экзабайта, как заявляют представители компании.

Это исследование показало, насколько плачевна ситуация в стане IPv6. Если верить полученным данным, объем междоменного IPv6 траффика составил всего 0.0026% от объема IPv4 траффика. Были замечены два пика активности между 4 ноября и Рождеством 2007 года, на пике процентное соотношение поднялось до 0.012%. При этом, процент соответствия IPv6 и IPv4 траффика оставался неизменным за все время исследования.

Я уже писал о том, что такое IPv6 и насколько переход на него с устаревшего IPv4 важен. В действительности, если не будет существенных сдвигов в ближайшее время, то по некоторым оценкам уже к 2011 году IPv4 адреса закончатся и развитие сети Интернет остановится. Переход же на IPv6 даст возможность использовать 340 миллиардов миллиардов миллиардов миллиардов адресов, чего должно хватить на ближайшее будущее :)

"Я не думаю, что переход уже начался", - говорит Scott Iekel-Johnson, основной автор исследования и главный специалист по программному обеспечению в компании Arbor, -"Не похоже, что хотя бы в какой-нибудь значительной части из охваченных исследованием регионов были существенные сдвиги"
Как это ни дико, по заявлениям исследователей, один из пиков совпал с собранием рабочей группы проектирования Интернет (IETF) - одной из групп, которая яростно толкает индустрию к использованию IPv6 адресования. Во время собрания, как говорят представители Arbor, участников попросили выключить IPv4 функциональность на своих компьютерах и роутерах и проверить, какие сайты будут доступны. Чтобы протестировать инфраструктуру, они смотрят онлайн-видео, качают файлы больших объемов. Предполагается, что это и вызвало учтенный пик.

Тем не менее, на этом собрании было зарегистрировано только 1168 человек. Т.е., по данным компании, всего чуть более тысячи человек вызвали самый высокий всплеск IPv6 активности за последние 12 месяцев. Это говорит о многом.

Исследователи видят два способа разрешения проблемы. Первый - поддержка идеи компаниями и государственными органами. Второй - проникновение идеи глубоко в массы. Iekel-Johnson делает ставку на первый вариант. Он приводит пример: "Если Comcast(ISP - прим. моё) скажет своим клиентам: 'Окей, вам нужно использовать IPv6, потому что у нас кончаются адреса, а мы хотим добавлять новых пользователей' - это приведет к существенным продвижениям".
Со своей стороны хочется добавить, что это исследование раскрыло мне глаза на то, насколько все плохо с IPv6. Возможно, понимание того, что мы еще в самом начале пути, подстегнет крупные компании, представителей государства и простых пользователей и продвижение пойдет быстрее.
Чтобы избежать обвинения в искажении фактов при адаптации я предлагаю поучаствовать в полном переводе статьи - сам я не смогу довести ее до читабельного вида без существенных правок. Поучаствовать и почитать текущий вариант перевода можно здесь

Friday, August 22, 2008

Использование Boost Python

“...one of the most highly regarded and expertly designed C++ library projects in the world.”
— Herb Sutter and Andrei Alexandrescu, C++ Coding Standards
Действительно, библиотека Boost - это живой пример того, насколько С++ может быть крут. И Boost.Python - не исключение.
Итак, Boost.Python - это библиотека, которая позволяет писать на С++ модули-расширения для Python, а также использовать возможности Python из С++. Зачем это может быть нужно? Например,
  • Переписывание bottlenecks на С/C++
  • Сокрытие части реализации в компилируемом модуле(весьма важно для распространениея коммерческих приложений)
  • Использование существующих библиотек(как на С/С++, так и на Python)
Конечно, можно использовать для решения этих задач Python C API, но, с одной стороны, С++ куда удобней для всего цикла разработки, а с другой - Boost.Python делает разработку на С++ настолько простой, что отказаться от нее невозможно.
Этим постом я не хочу повторять документацию по Boost.Python. Я ставлю целью скорее показать мощность Boost.Python и то, как он преобразует все вокруг :) И так как код - лучшая демонстрация, то я буду использовать фрагменты кода с пояснениями.
Итак, простейший модуль-расширение на С++:
#include <boost/python.hpp>
using namespace boost::python;
struct World
{
World(std::string msg_): msg(msg_) {}
std::string greet()
{
return msg;
}
std::string msg;
};
BOOST_PYTHON_MODULE(имя_модуля)
{
class_<World>("World", init<std::string>())
.def("greet", &World::greet)
;
}
Этот пример должен быть знаком тем, кто заглядывал в документацию. В нем ничего сложного нет - наш модуль включает в себя класс с конструктором и одной функцией. Предлагаю его взять в качестве основы и добавлять в него фрагменты ниже. Но сначала - как собирать этот пример. Предлагаю сохранить этот файл как имя_файла.cpp, после этого сборка может производиться при помощи gcc так:
gcc -shared -Wl,-soname,имя_модуля.so -o имя_модуля.so имя_файла.cpp -I /usr/include/python2.5/ -lboost_python
Обратите внимание, что имя_модуля должно совпадать с именем, указанным в файле. Также я использую python2.5. Тем, кто использует другую версию, нужно также поменять номер версии в пути для include.
Естественно, модуль не обязан состоять из одного файла, однако для сборки больших проектов рекомендую использовать distutils. Документация по нему достаточно подробная, чтобы собрать любой модуль. Самое главное - не забыть в описание Extension включить libraries=['boost_python'], а также указать то же имя Extension, что и внутри файлов используется.
После того, как простейший модуль собран и опробован,
In [1]: from имя_модуля import World
In [2]: w = World("Hello!")
In [3]: w.greet()
Out[3]: 'Hello!'
, можно приступать к самому интересному - к полезным обрывкам кода:
/*добавим две функции к World, "экспорт" их описывать не буду, можно по аналогии*/
void set(std::string msg) { this->msg = msg; }
void anotherWorldSet(World * world, std::string msg)
{
world->set(msg);
}
Проверяем:
In [1]: from hello_ext import World
In [2]: w1 = World("w1")
In [3]: w2 = World("w2")
In [4]: w1.anotherWorldSet(w2,"w2_new")
In [5]: w2.greet()
Out[5]: 'w2_new'
Т.е., Boost.Python прекрасно понимает, когда вы хотите получить объект по значению, а когда по ссылке. Более того, можно использовать и ссылки (void anotherWorldSet(World& world, std::string msg);) - разницы нет. Дальше перейдем к самому интересному - к boost::python::object. Это класс, который эмулирует работу Python объекта. Вот так, например:
void anotherObjectSet(object obj, std::string msg)
{
obj.attr("set")(msg);
}
И это работает, также как и предыдущий вариант. Обратите внимание на метод attr(). Его можно использовать как для вызова методов(см. выше), так и для получения значений:
std::string get_version()
{
/*Так импортятся Python модули. В качестве имени можно использовать и разделенные точкой названия*/
object sys = import("sys");
return extract<std::string>(sys.attr("version"));/*так преобразуются Python объекты в С++ объекты*/
}
std::string get_some(object myObj,std::string str)/*еще один пример*/
{
return extract<std::string>(myObj.attr(str.c_str()));
}
Раз уж зашла речь о Python объектах, то стоит рассказать о Python str и dict объектах:
dict foo_dict()
{
dict result;
result["1"]="2";
result["2"]="3";
return result;
}
object foo_string()
{
char buf[12]={0};
sprintf(buf,"%08XHELP",4242);
str s(buf,12);/*12 - длина строки*/
std::vector m;
m.push_back('1');m.push_back('2');m.push_back('3');m.push_back('4');
s += str(&m[0],m.size());
s += str("super test 2",12);
return s;
}
Т.е., работать с dict и str - одно удовольствие. Идем дальше, одна из пречудеснейших возможностей:
object get_func()
{
return make_function(&World::func);/*make_function - это чистая магия :)*/
}
std::string func()
{
return "I am func!";
}
Использование предыдущих фрагментов я не демонстрировал из Python, так как они очень просты. Представленный выше фрагмент ничуть не сложнее, но я все же покажу, как его использовать:
In [1]: from hello_ext import World
In [2]: w = World("Hello")
In [3]: f = w.get_func()
In [4]: f(w)
Out[4]: 'I am func!'
Обратите внимание на вызов f(w), здесь w - это self.
В общем, основные моменты я осветил. Многое еще можно найти в документации, во многом можно положиться на Boost.Python - он работает именно так, как вы от него ожидаете(например, C++ исключения транслируются в Python и т.д.).
Отдельно хочется упомянуть о нескольких важных вещах, которые могут привести к ошибкам:
  • В документации прекрасно описано наследование в Python от С++ классов. Однако, если вы переопределяете конструктор - обязательно вызывайте конструктор базового класса, даже если в базовом С++ классе он тривиален. Пренебрежение этим может вызвать непонятные ошибки
  • Если вы передаете Python объект по ссылке в С++ модуль, а потом сохраняете его там для дальнейшего использования - помните, что ссылка может стать невалидной - Python GC может и удалить его, если ссылка останется лишь в С++ части
Возможно, я буду обновлять этот список интересных моментов.
Ну, и на десерт. Если вы используете совместно С++ и Python модули (например, С++ модули используются для сокрытия функционала) и если вы используете логгирование, то скорее всего, вам захочется, чтобы логгирование всех модулей велось однотипно. Очень удобно было бы для этого использовать стандартный Python модуль logging. Чтобы было легче использовать этот модуль из С++, я написал небольшой класс:
/*----------------------------------------*/
/*logging.hpp*/
/*----------------------------------------*/
#pragma once
#include <boost/python.hpp>
#include <map>
#include <string>

class Logging
{
public:
static boost::python::object getLogger(const std::string& name);
static boost::python::object debug(const std::string& name);
static boost::python::object info(const std::string& name);
static boost::python::object warning(const std::string& name);
static boost::python::object error(const std::string& name);
static boost::python::object critical(const std::string& name);
static boost::python::object log(const std::string& name);

private:
typedef std::map<std::string,boost::python::object> LoggersMap;
static LoggersMap m_LoggersMap;
};
/*----------------------------------------*/
/*logging.cpp*/
/*----------------------------------------*/
#include "logging.hpp"

Logging::LoggersMap Logging::m_LoggersMap;

boost::python::object Logging::getLogger(const std::string& name)
{
static boost::python::object logging = boost::python::import("logging");
static boost::python::object settings = boost::python::import("conf.settings");/*Допустим, здесь хранятся настройки, в том числе и настройки логгирования*/

LoggersMap::iterator it = m_LoggersMap.find(name);
if(it != m_LoggersMap.end())
{
return (*it).second;
}
else
{
boost::python::object logger = logging.attr("getLogger")(name);
boost::python::object level = settings.attr("LOG_MODULES")[name];/*LOG_MODULES - dict, который ставит имя в соответствие с logging level*/
logger.attr("setLevel")(level);
m_LoggersMap[name] = logger;
return logger;
}
}

boost::python::object Logging::debug(const std::string& name)
{
return getLogger(name).attr("debug");
}

boost::python::object Logging::info(const std::string& name)
{
return getLogger(name).attr("info");
}

boost::python::object Logging::warning(const std::string& name)
{
return getLogger(name).attr("warning");
}

boost::python::object Logging::error(const std::string& name)
{
return getLogger(name).attr("error");
}

boost::python::object Logging::critical(const std::string& name)
{
return getLogger(name).attr("critical");
}

boost::python::object Logging::log(const std::string& name)
{
return getLogger(name).attr("log");
}
Использовать этот код очень просто:
Logging::getLogger("SomeLogger").attr("debug")("1.Logging stuff %s %s\n","1","2");
Logging::info("SomeLogger")("2.Logging stuff %s %s\n","1","2");
Logging::debug("SomeLogger2")("3.Logging stuff %s %s\n","1","2");
Logging::warning("SomeLogger")("4.Logging stuff %s %s\n","1","2");
Logging::error("SomeLogger2")("5.Logging stuff %s %s\n","1","2");
Logging::critical("SomeLogger2")("6.Logging stuff %s %s\n","1","2");

Спасибо за внимание! Буду рад любым исправлениям, корректировкам и дополнениям.