Boost logo

Boost Users :

From: Stanislas RENAN (stanislas.renan_at_[hidden])
Date: 2004-12-20 05:35:54


Hi,

I've got a performance problem during a test I've done with multi_index
on MSVC 6.0 SP 6.
I'm using BOOST 1.32.0 .
The search performance is comparable with several maps, but
insertion is dramatically slow : 5mn 30s for 10 000 employees.

Here is my test code :

===
/* Boost.MultiIndex basic example.
  *
  * Copyright 2003-2004 JoaquĂ­n M LĂłpez Muñoz.
  * Distributed under the Boost Software License, Version 1.0.
  * (See accompanying file LICENSE_1_0.txt or copy at
  * http://www.boost.org/LICENSE_1_0.txt)
  *
  * See http://www.boost.org/libs/multi_index for library home page.
  */
#include "stdafx.h"

#if !defined(NDEBUG)
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
#endif

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>

#include <cstdlib>
#include <map>
#include <vector>
#include <ctime>
#include <sstream>
#include <iomanip>

using boost::multi_index_container;
using namespace boost::multi_index;

/* an employee record holds its ID, name and age */

struct employee
{
   int m_id;
   std::string m_name;
   int m_age;

   employee(int id_,const std::string& name_,int
age_):m_id(id_),m_name(name_),m_age(age_){}

   void set(int id_,const std::string& name_,int age_) { m_id=id_;
m_name=name_; m_age=age_; }
   const employee& operator =(const employee& e) { set(e.m_id, e.m_name,
e.m_age); return *this; }
   employee(const employee& e) { set(e.m_id, e.m_name, e.m_age); }

   int id() const { return m_id; } // impossible d'appeler la fonction
et l'index "id"
   const std::string& name() const { return m_name; }
   int age() const { return m_age; }

   friend std::ostream& operator<<(std::ostream& os,const employee& e)
   {
     os<<e.m_id<<" "<<e.m_name<<" "<<e.m_age<<std::endl;
     return os;
   }
};

/* tags for accessing the corresponding indices of employee_set */

struct id{};
struct name{};
struct age{};

/* see Advanced topics: Use of member_offset for info on
  * BOOST_MULTI_INDEX_MEMBER
  */

/* Define a multi_index_container of employees with following indices:
  * - a unique index sorted by employee::int,
  * - a non-unique index sorted by employee::name,
  * - a non-unique index sorted by employee::age.
  */

typedef multi_index_container<
   employee,
   indexed_by<
     ordered_unique<
       tag<id>, BOOST_MULTI_INDEX_MEMBER(employee,int,m_id)>,
     ordered_non_unique<
       tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,m_name)>,
     ordered_non_unique<
       tag<age>, BOOST_MULTI_INDEX_MEMBER(employee,int,m_age)> >
> employee_set;

template<typename Tag,typename MultiIndexContainer>
void print_out_by(
  const MultiIndexContainer& s,
  Tag* =0 /* fixes a MSVC++ 6.0 bug with implicit template function parms */
)
{
   /* obtain a reference to the index tagged by Tag */

   const typename
boost::multi_index::index<MultiIndexContainer,Tag>::type& i=
     get<Tag>(s);

   typedef typename MultiIndexContainer::value_type value_type;

   /* dump the elements of the index to cout */

std::copy(i.begin(),i.end(),std::ostream_iterator<value_type>(std::cout));
}

// Un conteneur qui permet de contenir des employee
// triés sur 3 index.
class EmployeeMapper
{
   std::map<int, employee *> m_byId;
   std::map<std::string, employee *> m_byName;
   std::map<int, employee *> m_byAge;
   typedef std::map<int, employee *>::iterator EMIdIt;
   typedef std::map<std::string, employee *>::iterator EMNameIt;
   typedef std::map<int, employee *>::iterator EMAgeIt;

public:
   EmployeeMapper() {}
   ~EmployeeMapper()
   {
     // Deux lignes pour Ă©viter que ces objets ne contiennent
     // des pointeurs invalides pendant la destruction va un
     // autre conteneur.
     m_byName.clear();
     m_byAge.clear();

     std::map<int, employee *>::iterator it;
     while ((it=m_byId.begin()) != m_byId.end())
     {
       delete it->second;
       m_byId.erase(it);
     }
   }

   void add(const employee& e)
   {
     employee * e2 = NULL;
     try
     {
       e2 = new employee(e);
     }
     catch(...)
     {
       delete e2;
       throw;
     }
     m_byId[e2->id()] = e2;
     m_byName[e2->name()] = e2;
     m_byAge[e2->age()] = e2;
   }

   employee* byId(int id)
   {
     EMIdIt it = m_byId.find(id);
     if (it == m_byId.end())
       return NULL;
     else
       return it->second;
   }

   employee* byName(const std::string& name)
   {
     EMNameIt it = m_byName.find(name);
     if (it == m_byName.end())
       return NULL;
     else
       return it->second;
   }

   employee* byAge(int age)
   {
     EMAgeIt it = m_byAge.find(age);
     if (it == m_byAge.end())
       return NULL;
     else
       return it->second;
   }

private:
   // fonctions inhibĂ©es
   EmployeeMapper(const EmployeeMapper&);
   const EmployeeMapper& operator=(const EmployeeMapper&);
};

// Retourne un entier tq : 0 <= entier < maximum
int alea(int maximum)
{
   return int(std::min(maximum, RAND_MAX) * rand() / (RAND_MAX + 1.0));
}

int main()
{
   // Init alĂ©atoire
   srand( (unsigned)time( NULL ) );

   employee_set es;
   EmployeeMapper em;
   const int tailleEnsemble = 10000;
   const int tailleEchantillon = 10;
   const int moduloEchantillon = tailleEnsemble / tailleEchantillon;
   const int nbRecherches = 10000;

   std::vector<std::string> noms;
   noms.push_back("Alphonse");
   noms.push_back("ÉlĂ©onore");
   noms.push_back("Gaston");
   noms.push_back("SĂ©verine");
   noms.push_back("Zorglub");

   std::vector<employee> jeuDeDepart;
   std::vector<employee> jeuARechercher;

   int i=0;
   for (i=0; i < tailleEnsemble; ++i)
   {
     std::ostringstream os;
     // Construction d'un nom unique avec un caractère alĂ©atoire
     // Cela reflète le type de donnĂ©es gĂ©rĂ©es en rĂ©alitĂ©.
     // L'ajout de i implique l'unicitĂ©.
     os << noms[alea(5)] << std::setw(5) << std::setfill('0') << alea(30000)
                         << std::setw(5) << std::setfill('0') << i;
     employee e(i, // index numĂ©rique unique
                os.str(), // nom, chaĂ®ne unique
                alea(100)); // valeur potentiellement dupliquĂ©e

     jeuDeDepart.push_back(e);

     // On conserve un Ă©chantillon Ă  rechercher plus tard
     if (0 == i % moduloEchantillon)
     {
       //employee_set::iterator it=get<id>(es).find(i);
       //if (it != es.end())
         jeuARechercher.push_back(jeuDeDepart[i]);

     }
   }
   clock_t tes1, tes2;
   tes1 = clock();

   for (i=0; i < tailleEnsemble; ++i)
   {
     es.insert(jeuDeDepart[i]);
   }
   tes2 = clock();

   clock_t tem1, tem2;
   tem1 = clock();
   for (i=0; i < tailleEnsemble; ++i)
   {
     em.add(jeuDeDepart[i]);
   }
   tem2 = clock();

   clock_t t1_1, t2_1;
   t1_1 = clock();
   // Accède Ă  un certain nombre d'Ă©lĂ©ments prĂ©cis selon
   // plusieurs index
   for (i=0; i < nbRecherches; ++i)
   {
     for (std::vector<employee>::iterator it = jeuARechercher.begin();
          it != jeuARechercher.end();
          ++it)
     {
       // Les fonctions membres template qui permette de trouver les
       // index par tag ne fonctionnent pas avec MSVC 6
       employee_set::iterator eit = get<id>(es).find(it->id());
       nth_index_iterator<employee_set,1>::type eit2 =
get<1>(es).find(it->name());
       nth_index_iterator<employee_set,2>::type eit3 =
get<2>(es).find(it->age());
     }
   }
   t2_1 = clock();

   clock_t t1_2, t2_2;
   t1_2 = clock();
   // Deuxième mĂ©thode : avec des maps et des news.
   for (i=0; i < nbRecherches; ++i)
   {
     for (std::vector<employee>::iterator it = jeuARechercher.begin();
          it != jeuARechercher.end();
          ++it)
     {
       em.byId(it->id());
       em.byName(it->name());
       em.byAge(it->age());
     }
   }
   t2_2 = clock();

   double d1, d2;
   d1 = t1_2 - t1_1;
   d2 = t2_2 - t2_1;

   std::cout << "Insertion" << std::endl;
   std::cout << "multi = " << (tes2-tes1) << std::endl
             << "STL = " << (tem2-tem1) << std::endl;
   std::cout << "Recherche" << std::endl;
   std::cout << "multi = " << d1 << std::endl
             << "STL = " << d2 << std::endl;
   return 0;
}
===

Here are the results displayed :

===
Insertion
multi = 329125
STL = 156
Recherche
multi = 735
STL = 625
===

Am I doing something wrong ?
Note that I do not instanciate anything explicitely during insertion
loop.

Regards

-- 
Stanislas RENAN

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net