Image Image Image Image Image
Scroll to Top

Topo

boostpython

19

mar
2012

Sem Comentários

Em Blog
Código

Por Allison

Otimizando o Django Views com C++

Em 19, mar 2012 | Sem Comentários | Em Blog, Código | Por Allison

Fonte: IMasters

Texto original disponível em: http://www.jeffknupp.com/blog/2012/02/15/optimizing-django-views-with-c-plus-plus/


No meu artigo anterior, esbocei o método que vai sobre o perfil de um aplicativo Django. Utilizei uma view do linkrdr como um exemplo. Ela é responsável por agregar, classificar e ordenar todos os links de feeds de um usuário (RSS, Atom, Twitter, etc). O código a partir do envio da mensagem foi uma implementação simplista e inicial do panorama. Tenho, no entanto, um algoritmo de pontuação muito mais robusto, escrito em Python, que eu planejava utilizar no site.

Você pode ter percebido a palavra “planejado” ali, né? Pois é, o algoritmo se revelou muito lento. Mais do que eu consideraria aceitável. Depois de pensar em várias mudanças arquitetônicas que poderiam ser feitas para resolver o problema, cheguei em uma solução um tanto radical para um desenvolvedor Django: eu implementei a visão em C++.

Sei que nem todo desenvolvedor Django sabe C++ – e não precisa -, mas aqueles que sabem precisam perceber que é uma ferramenta viável quando Python está muito lento. Mas, eventualmente, você pode chegar a um ponto onde não é possível realmente otimizar o seu código Python mais. Neste caso, o perfil mostra que a maioria de seu tempo é gasto em chamadas de bibliotecas Python. Depois de bater nesse ponto, ou você escreve um algoritmo ineficiente ou você tem um problema não adequado para Python.

Quando percebi que tinha atingido esse ponto com o meu código de visão, entrei em pânico. “O que mais há para fazer?”, me perguntava. Então me lembrei de um projeto de trabalho onde havia escrito alguns códigos C++ que fazem interface com o Python. De uma perspectiva técnica, não havia nada me impedindo de implementar alguns aspectos do meu app Django em C++ (além do fato de que ele é torturante para escrever quando se trata do Python). Uma vez que linkrdr é um projeto de uma só pessoa, não existem companheiros que precisam aprender o código. Sendo assim, eu sou livre para implementá-lo como eu desejar.

Configurando

Tendo escrito um código “puro” C++/Python de interoperabilidade Python antes, e não querendo ver Py_XDecRef novamente, decidi usar boost::Python. Para começar, eu me certifiquei de que tinha as bibliotecas mais recentes do Boost e uma versão atualizada de gcc para que eu pudesse usar as características C++ 11, que são de fato muito boas. Depois de construir a versão mais recente da biclioteca boost::python, eu realemnte aprendi a lidar com isso. E ela acabou sendo extremamente fácil.

O boost::python envolve uma série de tipos de dados em Python para você: “object” representa um objeto Python genérico, “lista” é uma lista, e assim por diante. Como o Python é tipado dinamicamente, não há realmente muitos lugares inteiramente dele. “Tudo é um objeto” significa que tudo é um boost::python::object e pode ser acessado desta forma.

Além de wrappers primitivo, o boost fornece um mecanismo claro e conciso para fazer classes e funções C++ visíveis para Python. Eu tinha uma classe simples no código de entrada do nome anterior do meu LinkScore. Era basicamente uma estrutura C com uma lista de objetos e um interger. O código C++ é:

using namespace boost::python;

class LinkScore
{
public:
LinkScore() {}
LinkScore(const object& link, int score) : score_(score)
{
links_.append(link);
}
list links_;
int score_;
};

Se você está pensando que meus data members deveriam ser privados, adivinhem: eu não me importo. Essa é alegria de trabalhar em código que só você vai usar. Você pede escrevê-lo e usá-lo como quiser.

Os detalhes

Enfim, o código com o qual o boost:python pode chamar o Python é:

class_<LinkScore>("LinkScore", init<object, int>())
.def_readwrite("links", &LinkScore::links_)
.def_readwrite("score", &LinkScore::score_);

Realmente, não poderia ser mais simples. A maneira de conseguir isso <Python.h> envolve a definição de uma estrutura com quarenta valores para declarar cada classe. Fiquei feliz em não ter que me preocupar com isso.

O atual código para a minha view é uma função gratuita chamada get_scores. Aqui está um breve trecho:

using namespace boost::python;
using namspace std;

class CompareObject {
public:
bool  operator()(const LinkScore& l, const LinkScore& r) { return l.score_ > r.score_; }
};

list get_scores(object links)
{
object utility = import("links.utility");
set<LinkScore, CompareObject> seen_links;
list python_seen_links;
for (int i = 0; i < len(links); ++i)
{
const object& link = links[i];
LinkScore score = LinkScore(link, score_link (link, links));
auto iter = seen_links.find(score);

if (iter != seen_links.end())
{
// Do stuff
}
else
{
// Do other stuff
}
}
// TODO: Optimize this
for (auto i = seen_links.begin(); i != seen_links.end(); ++i)
{
python_seen_links.append(*i);
}
return python_seen_links;

Se você conhece C++ e Python, é quase como ler um mix dos dois. A descrição acima, no entanto, é válida para o código C++ e é a interface que o Python usa para chamar minha biblioteca de pontuação. Para expor essa função para o Python, tudo que é necessário é def (“get_score”, get_score); dentro de um bloco BOOST_PYTHON_MODULE, que nomeia o módulo a ser importado.

Quando terminei de escrever o código C++, eu o compilei usando o gcc com a ferramenta de biuld bjam da biblicota boots; defini o meu LD_LIBRARY_PATH para captar o libboost_Python.so e liguei uma shell do manage.py (na verdade, um “shell_plus”). Usei o módulo cProfile para comparar a versão do C++ da view com a versão do Python da view. Os resultados foram satisfatórios: um aumento de oito vezes na velocidade com a versão C++.

Para acionar o código C++, eu só precisava ter certeza que o .so gerado estiva em minha PYTHON_PATH. Assim, poderia importá-lo como uma biblioteca normal do Python. Acrescentei-o ao meu views.py e meus testes unitários rodaram. Depois disso, comitei tudo e coloquei o novo código através dos seus passos no servidor de desenvolvimento web. O tempo de resposta melhorou visivelmente, com a view funcionando instantaneamente.

Wrap Up

Sei que esta não é uma opção de otimização disponível para todos, mas é uma opção interessante. O Python é uma linguagem fantástica e o Django é um framework muito bom. No geral, estou bastante contente com os resultados e como foi fácil de implementar. Me absterei de escrever mais código C++ para linkrdr, a menos que seja absolutamente necessário. É bom saber, no entanto, que a opção está lá.

Tags | , , , ,