|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
[C++ / std:sort()]
Buondì
La funzione sort() durante il processo di ordinamento, mi modifica gli elementi. In particolare, svuota dei vettori. Il mio intento è ordinare un vector<> di oggetti di tipo: Codice:
template<typename T>
class geneBase
{
...
bool operator<(geneBase b) {return(fitness < b.GetFitness());}
...
vector<T> *code;
};
In un'altra classe definisco il vettore di questi elementi e utilizzo sort(): Codice:
template <typename T>
class populationBase
{
...
vector<geneBase<T>> population;
...
};
Codice:
std::sort(population.begin(), population.end()); non devo usare un puntatore nella dichiarazione? grazie dell'aiuto. |
|
|
|
|
|
#2 | |
|
Senior Member
Iscritto dal: Jan 2012
Messaggi: 1267
|
Quote:
Hai definito un copy constructor e un operator=(const geneBase&) per la tua classe? Se sì, posta il codice, il problema è lì. Perchè std::sort usa std::swap, che è definita così: Codice:
template<class T> void swap(T& a, T& b)
{
T temp = a; // Inizializzo temp copiando a, usando il costruttore geneBase(const geneBase&)
a = b; // Assegno b ad a con operator=(const geneBase&)
b = temp; // Assegno temp a b con operator=(const geneBase&)
}
|
|
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Ciao, grazie della risposta.
Si, allora intanto ho risolto evitando l'uso del puntatore. ma dammi cmq un parere, ora l'operatore = è definito in questo modo: Codice:
template <typename T>
geneBase<T>& geneBase<T>::operator=(const geneBase<T>& obj)
{
if(&obj != this)
{
// Copy elements
this->code.clear();
this->code = obj.code;
}
return *this;
}
|
|
|
|
|
|
#4 | |
|
Senior Member
Iscritto dal: Jan 2012
Messaggi: 1267
|
Quote:
Cmq copiare interi vettori può essere pesante, cosa ci devi fare? Ultima modifica di vendettaaaaa : 26-06-2013 alle 16:19. |
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
sono algoritmi genetici... risolvono problemi di ottimizzazione combinatoria.
allora elimino il clear()! In questo caso ordino l'array, si in effetti ci sono molti casi in cui devo copiare vettori, e poi ogni gene è una sequenza di valori quindi sostanzialmente il tutto è un vector di vector: vector<vector<bool>> per esempio quindi come dici, a questo punto meglio usare un puntatore dentro la classe, come avevo fatto all'inizio e modificare gli operatori vari... no? |
|
|
|
|
|
#6 | |
|
Senior Member
Iscritto dal: Jan 2012
Messaggi: 1267
|
Quote:
Codice:
geneBase(geneBase&& other) : code(std::move(other.code)) { // Altro }
Altrimenti bisogna metter mano ai puntatori e non è il massimo! Ultima modifica di vendettaaaaa : 27-06-2013 alle 16:43. |
|
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
si C++ 11, grazie approfondisco.
|
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Ho applicato il tuo consiglio,
inoltre in tutte le altre operazioni di copa sto usando la nuova std::move(). La velocità è aumentata. Una cosa che mi spiego ancora poco... un programma in C# similare che avevo fatto in passato è molto ma molto più veloce... mi domando come può essere.. c'è una differenza sostanziale nel come vengono copiati gli array?? se non erro una list in C# è simile un stl vector in c++. |
|
|
|
|
|
#9 | |
|
Senior Member
Iscritto dal: Jan 2012
Messaggi: 1267
|
Quote:
|
|
|
|
|
|
|
#10 | |
|
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
Una condizione più simile al C# la ottieni usando gli smart pointer unique_ptr o shared_ptr. Attenzione all'abuso della move semantic che è pensata per eliminare la doppia copia di oggetti temporanei e realizzare il perfect forwarding. Spostare un oggetto lascia l'originale in uno stato consistente ma indefinito... |
|
|
|
|
|
|
#11 | |
|
Senior Member
Iscritto dal: Aug 2003
Città: Barletta (BA)
Messaggi: 939
|
Gli array nella CLR funziona come gli array in C/C++ solamente se contengono value type (ricordo che String nonostante è classificato come value type, nella realtà è un referecence type)
http://www.codeproject.com/Articles/...R-Perspecti#20 In tutti gli altri casi, non è altro che un array di puntatori Quote:
Codice:
/**
* @brief Swaps two values.
* @param __a A thing of arbitrary type.
* @param __b Another thing of arbitrary type.
* @return Nothing.
*/
template<typename _Tp>
inline void
swap(_Tp& __a, _Tp& __b)
#if __cplusplus >= 201103L
noexcept(__and_<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>::value)
#endif
{
// concept requirements
__glibcxx_function_requires(_SGIAssignableConcept<_Tp>)
_Tp __tmp = _GLIBCXX_MOVE(__a);
__a = _GLIBCXX_MOVE(__b);
__b = _GLIBCXX_MOVE(__tmp);
}
__________________
In a world without fences, who needs Gates? Power by: Fedora 8 - Mac OS X 10.4.11 Ultima modifica di nico159 : 06-07-2013 alle 16:35. |
|
|
|
|
|
|
#12 | |||
|
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
Quote:
Quote:
Lo swap è un caso applicativo in cui torna utile evitare le copie di troppo |
|||
|
|
|
|
|
#13 | ||||
|
Senior Member
Iscritto dal: Aug 2003
Città: Barletta (BA)
Messaggi: 939
|
Quote:
Ho una pessima memoria Quote:
Quote:
Quote:
__________________
In a world without fences, who needs Gates? Power by: Fedora 8 - Mac OS X 10.4.11 Ultima modifica di nico159 : 07-07-2013 alle 14:19. |
||||
|
|
|
|
|
#14 | ||
|
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
Quando in C# copi una lista o un array ne copi il riferimento, quando in C++ copi un vector esegui una deep copy, mentre con gli array essendo fondamentalmente dei puntatori questo non succede. Quote:
|
||
|
|
|
|
|
#15 | |
|
Senior Member
Iscritto dal: Aug 2003
Città: Barletta (BA)
Messaggi: 939
|
Quote:
Ma forse stiamo sbagliando entrambi L'errore viene dal fatto che in C++ qualsiasi cosa può essere sia un value type o un reference type a seconda di come viene usato geneBase contiene un vector - quindi hai ragione tu? No geneBase contiene non un vector come di dici tu, ma un puntatore ad un vector Quindi perchè non si comporta come in C#? Perchè forse è stato affrontato il problema dal punto di vista sbagliato, come con l'operatore = che non permette di sfruttare questa situazione La soluzione migliore è quella di creare una specializzazione per std::swap che semplicemente scambi i due puntatori Meno overhead rispetto a move, e si sfrutta il fatto che quel vector non è un "value type" ma un "reference type"
__________________
In a world without fences, who needs Gates? Power by: Fedora 8 - Mac OS X 10.4.11 Ultima modifica di nico159 : 07-07-2013 alle 16:41. |
|
|
|
|
|
|
#16 | |
|
Senior Member
Iscritto dal: Jan 2012
Messaggi: 1267
|
Quote:
|
|
|
|
|
|
|
#17 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Data la differenza (notevole) di velocità, credo che il punto sia proprio che nel programma C++ CLR che ho scritto si fa una copia totale tutte le volte, quindi nel complesso rallenta molto.
Mentre in C# queste cose essendo già gestite il problema non mi è emerso... Dovrei provare a lavorare con i puntatori, che di norma ok non si fa, ma in questo caso dove si lavora ad intensità di dati, ovvero alla fine è un continuo modificare array, sia lacosa migliore. Ora ho anche un problemino di memoria, ovvero da qualche parte non sto rilasciando correttamente, dal debug noto che gli algoritmi di ordinamento fanno qualche pasticcio, prob. il mio operatore di copia non va bene. Per quanto riguarda il confronto fra C++ e C# trovai un bell'articolo qui: http://www.codeproject.com/Articles/...-Csharp-vs-NET |
|
|
|
|
|
#18 |
|
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Vi faccio vedere a pezzi, perchè il sorgente è un pò lungo...
Queste sono le due classi: Codice:
#include "libsann.h"
namespace ga
{
#pragma region Definitions
template<typename T>
class geneBase
{
public:
geneBase(void);
geneBase(uint len, double mrate = 0);
geneBase(geneBase&& other);
~geneBase(void);
// set, get and edit code
vector<T>* GetCode(void);
void SetCode(vector<T> *_code);
// Operators
bool operator<(geneBase b) { return(fitness < b.GetFitness()); }
geneBase& operator=(const geneBase& obj);
geneBase& operator+(const geneBase& obj);
geneBase& operator-(const geneBase& obj);
geneBase& operator+=(const geneBase& obj);
geneBase& operator-=(const geneBase& obj);
geneBase<bool>& operator+(geneBase<bool>& obj);
geneBase<bool>& operator-(geneBase<bool>& obj);
geneBase<bool>& operator+=(geneBase<bool>& obj);
geneBase<bool>& operator-=(geneBase<bool>& obj);
// Mutation
virtual void mutation(void);
// Access methods
inline double GetMutationRate(void) { return mate_rate; }
inline void SetMutationRate(double value) { mutation_rate = value; }
inline double GetFitness(void) { return fitness; }
inline void SetFitness(double value) { fitness = value; }
// Properties
inline uint lenght(void) { return code.size(); }
// Hamming distance
static double HammingDistance(geneBase<T>& a, geneBase<T>& b);
// Crossover
static geneBase& crossover(
geneBase<T>& a, geneBase<T>& b, crossover_mode m = crossover_mode::HAMMING, double mH = 0, uint pop_size = 0
);
protected:
// The code of gene
vector<T> code;
// The fitness value
double fitness;
// Mutation rate
double mutation_rate;
};
template <typename T>
class populationBase
{
public:
populationBase(double mrate = 0.05, double elitism_r = 0.1);
populationBase(uint size, uint len, double mrate = 0.05, double elitism_r = 0.1); // Auto-initialize population
populationBase(vector<geneBase<T>> _pop, double mrate = 0.05, double elitism_r = 0.1);
populationBase(populationBase&& obj);
~populationBase(void);
// Operators
populationBase operator=(populationBase& obj);
populationBase operator+(populationBase& obj);
void join(const populationBase& obj);
// Mate method
void mate(crossover_mode mode = crossover_mode::HAMMING);
// Genetic drift
void BlockgeneticDrift(void);
// Properties
inline double GetDiversity(void) { return diversity; }
inline double GetMaxHammingDistance(void) { return MaxHammingDistance; }
inline uint Size(void) { return population.size(); }
geneBase<T> BestGene(void);
// Access methods
vector<geneBase<T>> *PopulationPtr(void);
protected:
// Population
vector<geneBase<T>> population;
// Maximum Hamming distance
double MaxHammingDistance;
// Elitism rate
double elitism_rate;
// Default mutation rate
double mutation_rate;
uint RunRouletteWheel(utility::MyRandom random, double total_fit);
};
#pragma endregion
Codice:
#pragma region Gene implementation
#pragma region Constructors / Destructors
template <typename T>
geneBase<T>::geneBase(void)
{
fitness = 0;
mutation_rate = 0;
}
template <typename T>
geneBase<T>::geneBase(geneBase&& other)
{
code.assign(other.code.begin(), other.code.end());
fitness = other.fitness;
mutation_rate = other.mutation_rate;
}
geneBase<double>::geneBase(uint len, double mrate)
{
for(uint i = 0; i < len; i++)
{
code.push_back(((double)rand() / (double)RAND_MAX));
}
fitness = 0;
mutation_rate = mrate;
}
geneBase<uint>::geneBase(uint len, double mrate)
{
for(uint i = 0; i < len; i++)
{
code.push_back((uint)rand());
}
fitness = 0;
mutation_rate = mrate;
}
geneBase<bool>::geneBase(uint len, double mrate)
{
for(uint i = 0; i < len; i++)
{
code.push_back((rand() < (RAND_MAX/2)) ? true : false);
}
fitness = 0;
mutation_rate = mrate;
}
geneBase<char>::geneBase(uint len, double mrate)
{
for(uint i = 0; i < len; i++)
{
code.push_back((char)((rand() % 88)+32));
}
fitness = 0;
mutation_rate = mrate;
}
template <typename T>
geneBase<T>::~geneBase(void)
{
code.erase(code.begin(),code.end());
}
#pragma endregion
#pragma region Operators
template <typename T>
geneBase<T>& geneBase<T>::operator=(const geneBase<T>& obj)
{
if(&obj != this)
{
// Copy elements
this->code.assign(obj.code.begin(),obj.code.end());
}
this->fitness = obj.fitness;
this->mutation_rate = obj.mutation_rate;
return *this;
}
template <typename T>
geneBase<T>& geneBase<T>::operator+(const geneBase<T>& obj)
{
_ASSERT(code.size() == obj.GetCode().size());
vector<T> *_code_ = new vector<T>();
for(uint i = 0; i < code.size(); i++)
{
_code_->push_back(code.at(i) + obj.GetCode()->at(i));
}
geneBase<T> *C = new geneBase<T>();
C->SetCode(_code);
return *C;
}
template <typename T>
geneBase<T>& geneBase<T>::operator-(const geneBase<T>& obj)
{
_ASSERT(code.size() == obj.GetCode().size());
vector<T> *_code_ = new vector<T>();
for(uint i = 0; i < code.size(); i++)
{
_code_.push_back(code.at(i) - obj.GetCode()->at(i));
}
geneBase<T> *C = new geneBase<T>();
C->SetCode(_code);
return *C;
}
template <typename T>
geneBase<T>& geneBase<T>::operator+=(const geneBase<T>& obj)
{
_ASSERT(code.size() == obj.GetCode().size());
for(uint i = 0; i < code.size(); i++)
{
code->at(i) += obj.GetCode()->at(i);
}
this->fitness = 0;
return *this;
}
template <typename T>
geneBase<T>& geneBase<T>::operator-=(const geneBase<T>& obj)
{
_ASSERT(code.size() == obj.GetCode().size());
for(uint i = 0; i < code.size(); i++)
{
code.at(i) -= obj.GetCode()->at(i);
}
this->fitness = 0;
return *this;
}
geneBase<bool>& geneBase<bool>::operator+(geneBase<bool>& obj)
{
_ASSERT(code.size() == obj.GetCode()->size());
vector<bool> *_code_ = new vector<bool>();
for(uint i = 0; i < code.size(); i++)
{
if(obj.GetCode()->at(i))
_code_->push_back(true);
else
_code_->push_back(code.at(i));
}
geneBase<bool> *C = new geneBase<bool>();
C->SetCode(_code_);
return *C;
}
geneBase<bool>& geneBase<bool>::operator-(geneBase<bool>& obj)
{
_ASSERT(code.size() == obj.GetCode()->size());
vector<bool> *_code_ = new vector<bool>();
for(uint i = 0; i < code.size(); i++)
{
if(!(obj.GetCode()->at(i)))
_code_->push_back(false);
else
_code_->push_back(code.at(i));
}
geneBase<bool> *C = new geneBase<bool>();
C->SetCode(_code_);
return *C;
}
geneBase<bool>& geneBase<bool>::operator+=(geneBase<bool>& obj)
{
_ASSERT(code.size() == obj.GetCode()->size());
for(uint i = 0; i < code.size(); i++)
{
if(obj.GetCode()->at(i))
code.at(i) = true;
}
this->fitness = 0;
return *this;
}
geneBase<bool>& geneBase<bool>::operator-=(geneBase<bool>& obj)
{
_ASSERT(code.size() == obj.GetCode()->size());
for(uint i = 0; i < code.size(); i++)
{
if(!(obj.GetCode()->at(i)))
code.at(i) = false;
}
this->fitness = 0;
return *this;
}
#pragma endregion
Non ho messo il codice inerente alla seconda classe, ma vale lo stesso discorso... dato che vi sono array di oggetti di tipo geneBase. Io credo che l'operatore = sia uno dei problemi. credo mi convenga usare i puntatori, faccio un test e vi dico Ultima modifica di Teo@Unix : 08-07-2013 alle 11:47. |
|
|
|
|
|
#19 |
|
Senior Member
Iscritto dal: Aug 2003
Città: Barletta (BA)
Messaggi: 939
|
Fossi in te:
Prima cosa: geneBase<T>::geneBase(void) Evita geneBase<T>::geneBase() E' corretto in C++ Costruttori semplici possono essere scritti come: Codice:
geneBase() : fitness(0), mutation_rate(0) { }
Codice:
template <typename T>
geneBase<T>::geneBase(geneBase&& other)
{
code.assign(other.code.begin(), other.code.end());
fitness = other.fitness;
mutation_rate = other.mutation_rate;
}
Quando usi swap, move e così via, devi "ricorsivamente" usare lo stesso costrutto sui membri "complessi" della classe Codice:
template <typename T>
geneBase<T>::geneBase(geneBase&& other)
{
code = std::move(other.code);
fitness = other.fitness;
mutation_rate = other.mutation_rate;
}
Codice:
template <typename T>
geneBase<T>::~geneBase(void)
{
code.erase(code.begin(),code.end());
}
Non c'è bisogno di fare questo, la vita di code è collegata alla vita di geneBase, appena verrà distrutto geneBase verrà chiamato in automatico il distruttore di code, che a sua volta chiamerà i distruttori degli ogetti interni Gli unici distruttori che vanno chiamati nel distruttore del loro container sono per i puntatori, ma con: delete puntatore; Codice:
geneBase<bool>& geneBase<bool>::operator+=(geneBase<bool>& obj)
{
_ASSERT(code.size() == obj.GetCode()->size());
for(uint i = 0; i < code.size(); i++)
{
if(obj.GetCode()->at(i))
code.at(i) = true;
}
this->fitness = 0;
return *this;
}
Non credo che potrai sfruttare a tuo favore questa caratteristica, ti conviene scegliere un'altra maniera per rappresentare i tuoi dati Quello che stai facendo qua, è veramente lento code ha lunghezza variabile durante il ciclo di vita dell'oggetto?
__________________
In a world without fences, who needs Gates? Power by: Fedora 8 - Mac OS X 10.4.11 Ultima modifica di nico159 : 08-07-2013 alle 16:49. |
|
|
|
|
|
#20 | ||
|
Senior Member
Iscritto dal: Aug 2003
Città: Barletta (BA)
Messaggi: 939
|
Quote:
Provengo anche io da C#, quindi credo che almeno in parte riusciremo a capirci a vicenda Quote:
Il comportamento che vedi non è causa CLR, ma causa layout dei tuoi oggetti Mi spiego, prima discutevo con tomminno perchè diceva "la differenza è..." Ed ovviamente questo è il punto centrale del discorso C#, Java ed i tanti linguaggi OOP con gc fanno dimenticare all'utente una cosa importantissima: capire come i propri dati sono rappresentati in ram Cos'è in C# un oggetto? Un contenitore di puntatori Le uniche cose che contiene veramente, sono i value type Immaginati ora il tuo List di geneBase in C# Cos'è un array di geneBase in C#? Un Array di puntatori Significa: [pointer, pointer, pointer...] Cos'è geneBase? Un misto di value type ed altri puntatori geneBase { fitness, mutation_rate, pointerTo_CodeList } Cos'è code? Un puntatore, che al suo interno ha { capacity, size, ...pointerTo_BoolArray } Cos'è un Array di Bool? [bool, bool...] In C++ cos' è un array di geneBase? [ { fitness, mutation_rate, { capacity, size, ..., pointerTo_Bitset }}, { fitness, mutation_rate, { capacity, size, ..., pointerTo_Bitset }} ] Quindi, alla fine, è anche meglio no? No Primo sta usando un bitset, quindi l'accesso ai singoli elmenti è LENTO Secondo, non stai sfruttando il fatto che internamente vector conservi il puntatore ad un array - hai usato mode sulla tua classe, ma hai poi copiato i dati del bitset Dovevi dare a tua volta move sul bitset - il bitset allora avrebbe sfruttato il fatto che internamente ha un puntatore ai dati Ma scusa, C++ non è sempre meglio? Non è il linguaggio a fare veloce un programma, ma gli algoritmi e il layout dei propri dati C++ permette di scrivere codice migliore rispetto a C# perchè hai pieno controllo del layout dei tuoi dati Quando si scrive un sw ad alte performance, il layout dei dati è una cosa fondamentale da considerare Oggi il collo di bottiglia di ogni software, è l'accesso ai dati Se vuoi un software performance, devi prima di tutto pensare al design delle tue strutture dati e come sono rappresentate in memoria MS al Build 2013 ha dedicato un talk al riguardo: Native Code Performance and Memory: The Elephant in the CPU http://channel9.msdn.com/Events/Build/2013/4-329 Nessun linguaggio può fare per te queste scelte, nessun compilatore Sei tu che sei chiamato a farlo C# ti impone una visione basata sui puntatori, che distrugge il data locality e quindi ogni speranza di avere buone performance E' una illusione pensare che C# faccia scelte per te. Ti impone solo una strada che genericamente dovrebbe andar bene per la maggior parte dei casi Se vuoi performance simili, devi usare solamente struct in C# che sono limitate e scomode da usare Ecco perchè, discutevo con tomminno sul fatto che il problema parte da altro - e credo che alla fine entrambi dicevamo la stessa cosa Altra cosa importante, i puntatori non sono il male Se l'oggetto che usa il puntatore segue la regola dei tre http://en.wikipedia.org/wiki/Rule_of...programming%29 non c'è possibilità di trovarsi in problemi Il problema si ha quando i puntatori non hanno un ciclo di vita chiaro al programmatore Ovvero il programmatore non sa, come il proprio algoritmo dovrà funzionare (incapacità? Troppa complessità?) e quindi si eliminano puntatori prima, si accedono a puntatori ormai eliminati, non si eliminano proprio, e così via Come vedi, la riflessione non è su C# vs C++ Ma più che altro un problema di programmazione pura: Gestione delle proprie strutture dati, layout dei dati, pattern di accesso ai dati, algoritmi
__________________
In a world without fences, who needs Gates? Power by: Fedora 8 - Mac OS X 10.4.11 Ultima modifica di nico159 : 08-07-2013 alle 16:56. |
||
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 19:05.




















