Recent Changes - Search:

Accueil

OpenSSL

SyncML

Apache Portable Runtime

Libxml2

Net-snmp

CUrl

Boost

Perl

ZLib

Samba

VPN

Serveurs de messagerie

edit

Boost/Boost-regex

Le moteur d'expressions régulières de Boost est très performant et de plus compilable sous différentes plateformes (dans mon cas AIX et Windows). Après quelques tests de validation, les performances étant au rendez-vous le choix d'utiliser cette librairie fût arrêté.

Dans le cadre de mon projet, l'objectif était de traiter plusieurs gigas octets de logs dans les meilleurs délais pour cela je décide d'implémenter la technique des memoryMappedFile. Cette technique d'accès aux fichiers est très performante, le principe est de mapper, par des appels à l'API système, la zone de données de l'application sur le disque dur de la même façon que le fait Windows avec sa mémoire virtuelle (swap disk). Bien que le disque soit d'une lenteur incomparable à celle de la mémoire vide, le gain de temps se fait sur l'allocation mémoire et le chargement des données ce qui donne des performances excellentes. Le gain de temps obtenu est de l'ordre de trois et n'est pas sensible à la taille du fichier traité (problématique du swap en cas de chargement en mémoire).
Il restait à résoudre la problématique de la portabilité de mon code source, je n'eu pas loin à chercher car dans la partie iostreams des librairie boost se trouve une libairie d'abstraction prête à l'emploit (http://www.boost.org/libs/iostreams/doc/index.html).

Pour vous donner une meilleur idée des performances du couple mmap/regex, vous trouverez ci-dessous un court exemple :


#ifdef _DEBUG
#pragma comment(lib,"..\\boost_1_33_1\\libs\\regex\\build\\vc6\\libboost_regex-vc6-mt-sgd-1_33_1.lib")
#else if
#pragma comment(lib,"..\\boost_1_33_1\\libs\\regex\\build\\vc6\\libboost_regex-vc6-s-1_33_1.lib")
#endif


#include <boost/iostreams/device/mapped_file.hpp>

#include <iostream>
#include <boost/timer.hpp>
#include <boost/regex.hpp>
#include <fstream>

using namespace std;
using namespace boost;
using namespace boost::iostreams;

// Sous Unix, configurer boost par
// cd <boost-root>/libs/config/
// sh ./configure
//
// pour la compilation :
// gcc mapfile.cpp -pedantic -O2 -L /usr/local/lib/ -I /usr/local/include -I ./ -I ../boost_1_33_1/  -o mapfile
// /usr/local/bin/g++ -o mapfile mapfile.cpp ../boost_1_33_1/libs/iostreams/src/mapped_file.cpp -pedantic -O2 -L /usr/local/lib/ -I /usr/local/include -I ./ -I ../boost_1_33_1/

typedef std::map<std::string, std::string::difference_type, std::less<std::string> > map_type;

#define REG_DATE """([[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2})"""
#define REG_TIME """([[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2})"""
#define REG_CIP """([[:digit:]]{1,3}\\.[[:digit:]]{1,3}\\.[[:digit:]]{1,3}\\.[[:digit:]]{1,3})"""
#define REG_USER """(-|[[:alnum:]]*)"""
#define REG_SITENAME """([[:alnum:]]*)"""
#define REG_SCOMPUTERNAME """([[:alnum:]]*)"""
#define REG_SIP """([[:digit:]]{1,3}\\.[[:digit:]]{1,3}\\.[[:digit:]]{1,3}\\.[[:digit:]]{1,3})"""
#define REG_SPORT """([[:digit:]]*)"""
#define REG_METHOD """([[:alpha:]]*)"""
#define REG_URI """([^[:space:]]*)"""
#define REG_REQ """(-|[^[:space:]]*)"""
#define REG_STATUS """([[:digit:]]{3})"""
#define REG_WIN32STATUS """([[:digit:]]*)"""
#define REG_SCBYTES """([[:digit:]]*)"""
#define REG_CSBYTES """([[:digit:]]*)"""
#define REG_DURATION """([[:digit:]]*)"""
#define REG_HTTP """([^[:space:]]*)"""
#define REG_HOST """([^[:space:]]*)"""
#define REG_USERAGNT """([^[:space:]]*)"""
#define REG_COOKIE """([^[:space:]]*)"""
#define REG_REFERER """([^[:space:]]*)"""

const char* re =  REG_DATE "[[:space:]]" REG_TIME "[[:space:]]" REG_CIP "[[:space:]]" \
        REG_USER "[[:space:]]" REG_SITENAME "[[:space:]]" REG_SCOMPUTERNAME "[[:space:]]" \
        REG_SIP "[[:space:]]" REG_SPORT "[[:space:]]" REG_METHOD "[[:space:]]" REG_URI  "[[:space:]]"\
        REG_REQ "[[:space:]]" REG_STATUS "[[:space:]]" REG_WIN32STATUS "[[:space:]]" REG_SCBYTES \
        "[[:space:]]" REG_CSBYTES "[[:space:]]" REG_DURATION "[[:space:]]" REG_HTTP \
        "[[:space:]]" REG_HOST "[[:space:]]" REG_USERAGNT "[[:space:]]" REG_COOKIE \
        "[[:space:]]" REG_REFERER ;



int i=0;

bool regex_callback(const boost::match_results<std::string::const_iterator>& what)
{
        //printf("\r\n\r\n%i\r\n%s\r\n",i,what[0].str());
        i++;
        return true;
}


int main(int argc, char* argv[])
{

    try
    {

                boost::regex expression(re);


                timer a;

                cout << "opening " << argv[1] << endl;





                a.restart();

                mapped_file_source mapfile;
                cout << "mapped file method" << endl;
                mapfile.open(argv[1]);
                boost::sregex_iterator m1(mapfile.data(), mapfile.data() +      mapfile.size(), expression);
                boost::sregex_iterator m2;
                std::for_each(m1, m2, &regex_callback);

                cout <<  i <<endl;

                mapfile.close();
                printf("elapse time : %5.3f seconds\n\r",a.elapsed());





                cout << "\n\rfileopen method" << endl;
                i=0;
                a.restart();
                ifstream file;
                file.open(argv[1],ios_base::in | ios_base::binary );
                std::string text;

                text.erase();
                text.reserve(file.rdbuf()->in_avail());
                {
                        char c;
                        while(file.get(c))
                        {
                                if(text.capacity() == text.size())
                                        text.reserve(text.capacity() * 3);
                                text.append(1, c);
                        }
                }


                boost::sregex_iterator m3(text.begin(), text.end(), expression);
                boost::sregex_iterator m4;
                std::for_each(m3, m4, &regex_callback);

                cout <<  i <<endl;
                file.close();
                printf("elapse time : %5.3f seconds\n\r",a.elapsed());



        }
        catch ( std::ios::failure &fail ){
                cout << "Caught an exception: " << fail.what () << endl;

        }
        return 0;
}


 

Le traitement effectué est l'analyse d'une log IIS par une expression régulière particulièrement longue.
Sur un fichier de 70Mo, sous Windows, le temps de traitement ne dépasse pas 12 secondes (à la première exécution, car un cache accélere grandement les choses par la suite) et atteint 30 secondes par la méthode classique. Sous AIX, le rapport entre les deux méthodes est très proche car je n'ai pas observé de variation entre deux exécutions. A noter, que bien que la machine AIX utilisée pour mes tests date d'environ six ans, les temps de traitement mesurés étaient deux fois meilleurs que ceux obtenus sous Windows ce qui semble montrer l'extrême importance des performances des disques (une baie de stockage connectée en fiber channel pour AIX et un disque IDE pour Windows).
Je ne ferai pas de tests sur d'autres plateformes mais serais curieux d'avoir une idée de la performance d'un tel traitement en Java, perl, php ....

Edit - History - Print - Recent Changes - Search
Page last modified on November 09, 2007, at 11:51 PM