Ещё о быстром move.
От: Шахтер Интернет  
Дата: 09.10.04 20:20
Оценка:
Я тут набросал пару примеров, как можно быстро двигать вектора.

Второй пример на VC++ 7.1 генерирует, по-видимому, неулучшаемый код.

; 114  :   
; 115  :   FastMoveVector(mem2,vec1,vec1+Size);

    lea    ecx, DWORD PTR _mem1$[esp+200]
    lea    edx, DWORD PTR _mem2$[esp+200]
    xor    eax, eax
    npad    8
$L14160:
    mov    esi, DWORD PTR _mem1$[esp+eax+196]
    mov    DWORD PTR _mem2$[esp+eax+196], esi
    mov    esi, DWORD PTR [ecx]
    add    eax, 8
    mov    DWORD PTR [edx], esi
    lea    esi, DWORD PTR _mem1$[esp+eax+196]
    lea    ebp, DWORD PTR _mem1$[esp+276]
    add    ecx, 8
    add    edx, 8
    cmp    esi, ebp
    jb    SHORT $L14160


-- это memcpy скорость.

Единственный момент здесь -- у меня есть неясность с одним абзацем в стандарте. 3.8 4

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a nontrivial destructor. For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

Пример 1.

/* MoveVector.cpp */ 

#include <iostream>

using namespace std;

/* Replace<T>() */ 

template <class T>
inline T Replace(T &var,T val) { T ret=var; var=val; return ret; }

/* class MoveSrc<T> */ 

template <class T>
class MoveSrc
 {
   T *src;
 
  public:
  
   MoveSrc(T &x) : src(&x) {}
   
   T * operator -> () const { return src; }
 };
 
/* class String */  

class String
 {
   char *str;
   size_t len;
   
   String(const String &) {}
   
   void operator = (const String &) {}
   
   static char * GetNull(); 
 
  public:
  
   String() : str(GetNull()),len(0) {}
   
   String(const char *str_);
   
   explicit String(MoveSrc<String> x)
    {
     str=Replace(x->str,GetNull());
     len=Replace(x->len,size_t(0));
    }
   
   ~String();
   
   const char * getStr() const { return str; }
   
   size_t getLen() const { return len; }
   
   String * moveObjectTo(void *mem)
    {
     __assume(mem);
     
     return new(mem) String(MoveSrc<String>(*this));
    }
 };

char * String::GetNull()
 {
  static char zero[1];
  
  return zero;
 }
 
String::String(const char *str_)
 {
  len=strlen(str_);
  
  str=new char[len+1];
  
  memcpy(str,str_,len+1);
  
  cout << "new char[" << len+1 << "]\n" ;
 }
   
String::~String()
 {
  if( str!=GetNull() ) 
    {
     delete[] str;
     
     cout << "delete char[]\n" ;
    }
 }
 
/* MoveVector<T>() */  

template <class T>
void MoveVector(void *mem,T *ptr,T *lim) throw()
 {
  for(; ptr<lim ;ptr++,mem=static_cast<char *>(mem)+sizeof (T)) 
    {
     ptr->moveObjectTo(mem);
     
     (*ptr).~T();
    }
 }
   
/* main() */ 

const size_t Size=10;

int main()
 {
  char mem1[Size][sizeof (String)];
  char mem2[Size][sizeof (String)];
  
  String *vec1=static_cast<String *>((void *)mem1);
  String *vec2=static_cast<String *>((void *)mem2);
  
  for(size_t i=0; i<Size ;i++) new(mem1[i]) String("string");
  
  for(size_t i=0; i<Size ;i++) cout << vec1[i].getStr() << '\n' ;
  
  MoveVector(mem2,vec1,vec1+Size);
 
  for(size_t i=0; i<Size ;i++) cout << vec2[i].getStr() << '\n' ;
  
  for(size_t i=0; i<Size ;i++) vec2[i].~String();
  
  return 0;
 }


Пример 2.

/* FastMoveVector.cpp */ 

#include <iostream>

using namespace std;

/* class FastMoveSrc<T> */ 

template <class T>
class FastMoveSrc
 {
   T *src;
 
  public:
  
   FastMoveSrc(T &x) : src(&x) {}
   
   T * operator -> () const { return src; }
 };
 
/* class String */  

class String
 {
   char *str;
   size_t len;
   
   String(const String &) {}
   
   void operator = (const String &) {}
   
   static char * GetNull(); 
 
  public:
  
   String() : str(GetNull()),len(0) {}
   
   String(const char *str_);
   
   explicit String(FastMoveSrc<String> x)
    {
     str=x->str;
     len=x->len;
         
     // Исходный объект после этой операции становится невалидным и не должен больше использлваться -- включая вызов деструктора
    }
   
   ~String();
   
   const char * getStr() const { return str; }
   
   size_t getLen() const { return len; }
   
   String * fastMoveObjectTo(void *mem)
    {
     __assume(mem);
     
     return new(mem) String(FastMoveSrc<String>(*this));
    }
 };

char * String::GetNull()
 {
  static char zero[1];
  
  return zero;
 }
 
String::String(const char *str_)
 {
  len=strlen(str_);
  
  str=new char[len+1];
  
  memcpy(str,str_,len+1);
  
  cout << "new char[" << len+1 << "]\n" ;
 }
   
String::~String()
 {
  if( str!=GetNull() ) 
    {
     delete[] str;
     
     cout << "delete char[]\n" ;
    }
 }
 
/* FastMoveVector<T>() */  

template <class T>
void FastMoveVector(void *mem,T *ptr,T *lim) throw()
 {
  for(; ptr<lim ;ptr++,mem=static_cast<char *>(mem)+sizeof (T)) 
    {
     ptr->fastMoveObjectTo(mem);
        
     // Считаем объект убитым и не вызываем деструктор.
    }
 }
   
/* main() */ 

const size_t Size=10;

int main()
 {
  char mem1[Size][sizeof (String)];
  char mem2[Size][sizeof (String)];
  
  String *vec1=static_cast<String *>((void *)mem1);
  String *vec2=static_cast<String *>((void *)mem2);
  
  for(size_t i=0; i<Size ;i++) new(mem1[i]) String("string");
  
  for(size_t i=0; i<Size ;i++) cout << vec1[i].getStr() << '\n' ;
  
  FastMoveVector(mem2,vec1,vec1+Size);
 
  for(size_t i=0; i<Size ;i++) cout << vec2[i].getStr() << '\n' ;
  
  for(size_t i=0; i<Size ;i++) vec2[i].~String();
  
  return 0;
 }
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Ещё о быстром move.
От: Shady Россия  
Дата: 09.10.04 20:58
Оценка:
Здравствуйте, Шахтер, Вы писали:

Наверное уже поздно, но я не могу понять, а где вы используете эффект от поведения деструктора?
... << RSDN@Home 1.1.3 stable >>
"Man feed machine
Machine feed man"
Peter Gabriel — OVO — The Tower That Ate People
Re[2]: Ещё о быстром move.
От: Шахтер Интернет  
Дата: 09.10.04 21:23
Оценка:
Здравствуйте, Shady, Вы писали:

S>Здравствуйте, Шахтер, Вы писали:


S>Наверное уже поздно,


Не совсем понял, что значит поздно.

S>но я не могу понять, а где вы используете эффект от поведения деструктора?


Нигде. Я просто не могу понять, что подразумевает стандарт под "побочными эффектами, производимыми деструктором", и что значит "зависеть от них".
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Ещё о быстром move.
От: Shady Россия  
Дата: 10.10.04 14:18
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Не совсем понял, что значит поздно.

По времени был час ночи...

S>>но я не могу понять, а где вы используете эффект от поведения деструктора?


Ш>Нигде. Я просто не могу понять, что подразумевает стандарт под "побочными эффектами, производимыми деструктором", и что значит "зависеть от них".

да, , самому интерестно...
... << RSDN@Home 1.1.3 stable >>
"Man feed machine
Machine feed man"
Peter Gabriel — OVO — The Tower That Ate People
Re: Ещё о быстром move.
От: Павел Кузнецов  
Дата: 12.10.04 05:13
Оценка:
Шахтер:

> неясность с одним абзацем в стандарте. 3.8 4


> <...> For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.


Грубо говоря, если:
  • в деструкторе есть существенные для программы побочные эффекты, выходящие за рамки деструктора (то есть, ближе к формулировке стандарта: программа будет выполняться по-разному в зависимости от того, вызван деструктор или нет)
  • и программа этот деструктор для старого объекта не вызвала перед тем, как создать на его месте новый,
    то в ней неопределенное поведение.
    Posted via RSDN NNTP Server 1.9 gamma
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[2]: Ещё о быстром move.
    От: Шахтер Интернет  
    Дата: 12.10.04 16:39
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Шахтер:


    >> неясность с одним абзацем в стандарте. 3.8 4


    >> <...> For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.


    ПК>Грубо говоря, если:

    ПК>
  • в деструкторе есть существенные для программы побочные эффекты, выходящие за рамки деструктора (то есть, ближе к формулировке стандарта: программа будет выполняться по-разному в зависимости от того, вызван деструктор или нет)
    ПК>
  • и программа этот деструктор для старого объекта не вызвала перед тем, как создать на его месте новый,
    ПК>то в ней неопределенное поведение.

    Пока что яснее не стало.
    ... << RSDN@Home 1.1.0 stable >>
  • В XXI век с CCore.
    Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.