пятница, 2 декабря 2011 г.

[prog] Слоистость, мать ее!

Есть чудный афоризм: любую проблему дизайна можно исправить добавлением дополнительных слоев абстракции, за исключением проблемы слишком большого количества этих самых слоев.

Этот афоризм вспомнился в попытках понять, почему select из временной таблички с сотней записей и двумя столбцами занимает десятки миллисекунд. Хотя более тяжелые запросы из больших таблиц, как правило, отрабатывают в три-четыре раза быстрее.

Есть MS SQL Server (не важно, 2005 или 2008), есть ODBC, над ODBC еще есть OTL. И простой select длится 50 миллисекунд (при том, что insert-ы – всего 7 миллисекунд). Какой слой здесь выступает тормозом? На кого вешать собак?… Тайна сия велика есть.

Павбывавбы, короче.

Под катом маленькая тестовая программка, на которой и получены эти удивительные времена.

#include <iostream>
#include <sstream>
#include <ctime>

#define OTL_EXCEPTION_DERIVED_FROM std::exception
#define OTL_EXCEPTION_HAS_MEMBERS \
   virtual const char * what() const throw() { \
      return reinterpret_cast< const char * >( this->msg ); \
   }

#define OTL_STREAM_POOLING_ON
#define OTL_STL
#define OTL_BIGINT long long
#define OTL_ODBC_MULTI_MODE
#include <otlv4.h>

std::string
make_a( int index )
   {
      std::ostringstream s;

      s << index;
      const std::string & v = s.str();

      return v + "-" + v + "--" + v + "---" + v + "----" + v;
   }

void
fill_table( otl_connect & db )
   {
      otl_stream stream( 100,
            "insert into #A(a, b) values(:a<char[65]>, :b<int>)",
            db );

      for( int i = 0; i != 100; ++i )
         {
            stream << make_a( i ) << i;
         }
   }

void
load_table( otl_connect & db )
   {
      otl_stream stream( 100,
            "select a, b from #A",
            db );

      while( !stream.eof() )
         {
            std::string a;
            int b;

            stream >> a >> b;
         }
   }

#define MEASURE(what) { \
   std::clock_t s = std::clock(); \
   what ; \
   std::clock_t f = std::clock(); \
   const double duration = double(f - s) / CLOCKS_PER_SEC; \
   std::cout << #what << ": " << duration << std::endl;  \
}

void
do_test()
   {
      otl_connect db;
      db.set_connection_mode( OTL_MSSQL_2005_ODBC_CONNECT );
      db.auto_commit_off();
      db.rlogon(
         "UID=test_user;PWD=CENSORED;DRIVER=SQL Native Client;SERVER=CENSORED;Database=CENSORED" );

      db.direct_exec(
         "create table #A (a varchar(64), b integer, "
            "constraint a_PK primary key(a,b) );"
      );

      MEASURE( fill_table(db); )
      MEASURE( load_table(db); )
   }

int
main()
   {
      try
         {
            do_test();
         }
      catch( const otl_exception & x )
         {
            std::cerr << "OTL Exception: " << x.what() << std::endl
                  << "SQLState: " << x.sqlstate << std::endl
                  << "STM_Text: " << x.stm_text << std::endl;

            return 1;
         }
      catch( const std::exception & x )
         {
            std::cerr << "Exception: " << x.what() << std::endl;

            return 1;
         }

      return 0;
   }
Отправить комментарий