2017-11-23 2 views
1

Ich habe die Aufgabe, die Serialisierung großer Sätze von Floats auf einer Festplatte zu optimieren. Mein erster Ansatz hat die folgenden:Stringstream-Serialisierung, auf CentOS, einer großen Menge von Floats ist schneller als 4 Pthread-ts, die Chunks serialisieren. std :: threads sind schneller unter Windows

class StringStreamDataSerializer 
{ 
public: 
void serializeRawData(const vector<float>& data); 
void saveToFileStream(std::fstream& file); 
private: 
stringstream _stringStream; 
}; 

void StringStreamDataSerializer::serializeRawData(const vector<float>& data) 
{ 
for (float currentFloat : data) 
    _stringStream << currentFloat; 
} 

void StringStreamDataSerializer::saveToFileStream(std::fstream& file) 
{ 
file << _stringStream.str().c_str(); 
file.close(); 
} 

ich die Aufgabe, serializaton zwischen 4 Threads, trennen wollte schneller die Serialisierung zu machen. Hier ist, wie:

struct st_args 
{ 
const vector<float>* data; 
size_t from; 
size_t to; 
size_t segment; 
} ; 

string outputs[4]; 
std::mutex g_display_mutex; 
void serializeLocal(void *context) 
{ 
struct st_args *readParams = (st_args*)context; 

for (auto i = readParams->from; i < readParams->to; i++) 
{ 
    string currentFloat = std::to_string(readParams->data->at(i)); 

    currentFloat.erase(currentFloat.find_last_not_of('0') + 1, 
    std::string::npos); 
    outputs[readParams->segment] += currentFloat; 
} 
} 

void SImplePThreadedSerializer::serializeRawData(const vector<float>& data) 
{ 
const int N = 4; 
size_t totalFloats = data.size(); 
st_args* seg; 
pthread_t* chunk; 

chunk = (pthread_t *) malloc(N*sizeof(pthread_t)); 
seg = (st_args *) malloc(N*sizeof(st_args)); 

size_t from = 0; 
for(int i = 0; i < N; i++) 
{ 
    seg[i].from = 0; 
    seg[i].data = &data; 
} 

int i = 0; 
for (; i < N - 1; ++i) 
{ 
    seg[i].from = from; 
    seg[i].to = seg[i].from + totalFloats/N; 
    seg[i].segment = i; 

    pthread_create(&chunk[i], NULL, (void *(*)(void *)) serializeLocal, 
(void *) &(seg[i])); 
    from += totalFloats/N; 
} 

seg[i].from = from; 
seg[i].to = totalFloats; 
seg[i].segment = i; 

pthread_create(&chunk[i], NULL, (void *(*)(void *)) serializeLocal, (void *) 
&(seg[i])); 

size_t totalBuffered = 0; 
for (int k = 0; k < N; k++) 
{ 
    pthread_join(chunk[k], NULL); 
    totalBuffered += outputs[k].size(); 
} 
str.reserve(totalBuffered); 
for (int k = 0; k < N; k++) 
{ 
    str+= outputs[k]; 
} 

free(chunk); 
free(seg); 
} 

Es stellt sich heraus, dass der string schneller sogar von 4 Thread auf Linux. Unter Windows archiviere ich eine Optimierung mit dem vorgestellten Ansatz (mit std :: thread) unter Windows, aber unter Linux habe ich die gegenteiligen Ergebnisse. Jede Erklärung warum wäre hilfreich und geschätzt.

Hier sind die Ergebnisse auf CentOS: * Serialisierung von 10.000.000 schwimmt auf der Festplatte * StringStreamDataSerializer Wallungen Daten in der Datei in 0,55 Sekunden. StringStreamDataSerializer In 3.28 Sekunden abgeschlossen. Der SimplePThreadedSerializer spült die Daten in der Datei in 0,46 Sekunden. SiMPplePThreadedSerializer Beendet in 6.96 Sekunden.

Unter Windows ist die Multi-Thread-Serialisierung von 4 std :: getan Fäden und sie optimieren tatsächlich die Serialisierung:

static void serializeChunk(string& output, const vector<float>& data, size_t 
from, size_t to) 
{ 
for (auto i = from; i < to; i++) 
{ 
    string currentFloat = std::to_string(data[i]); 

    //fuckin trim the zeroes at the end 
    currentFloat.erase(currentFloat.find_last_not_of('0') + 1, 
std::string::npos); 
    output += currentFloat; 
} 
} 

void SimpleMultiThreadedSerializer::serializeRawData(const vector<float>& 
data) 
{ 

const int N = 4; 
thread t[N]; // say, 4 CPUs. 
string outputs[N]; 
size_t totalFloats = data.size(); 

size_t from = 0; 

int i = 0; 
for (; i < N - 1; ++i) 
{ 
    t[i] = thread(serializeChunk, std::ref(outputs[i]), data, from, from + 
totalFloats/N); 
    from += totalFloats/N; 
} 
t[i] = thread(serializeChunk, std::ref(outputs[i]), data, from, 
totalFloats); 

for (i = 0; i < N; ++i) 
    t[i].join(); 

size_t totalBuffered = 0; 
for (int i = 0; i < N; ++i) 
    totalBuffered += outputs[i].size(); 

str.reserve(totalBuffered); 
for (int i = 0; i < N; ++i) 
    str += outputs[i]; 
} 

Und die Ergebnisse: * Serialisierung von 1000000 schwimmt auf der Festplatte * StringStreamDataSerializer löscht Daten in Datei in 0,116 Sekunden. StringStreamDataSerializer Beendet in 10.236 Sekunden.

SimpleMultiThreadedSerializer löscht Daten in Datei in 0.105 Sekunden. SimpleMultiThreadedSerializer Beendet in 3.01 Sekunden.

Antwort

0

Die Konvertierung zwischen binärem Fließkomma- und Dezimal-Ausgang ist sehr teuer. Wenn die Leistung ein Problem darstellt, sollten Sie die Daten in Binärform serialisieren (möglicherweise nach der Konvertierung der Endianität, sodass Sie zumindest Interoperabilität über IEEE 754-Systeme erhalten).

In Bezug auf die schlechte Threading unter GNU/Linux-Leistung ist dies wie ein known performance issue regarding locale object handling. Im Multithread-Modus verwendet stringstream derzeit einen prozessweiten, stark umkämpften Referenzzähler für die Gebietsschema-Behandlung.

+0

Vielen Dank für die Antwort! Ich denke, ich verstehe das Problem. Ich werde versuchen, die ftoa-Methode zu implementieren und zu verwenden, die entweder ein Gebietsschema-Objekt erhält und es möglicherweise sperrt oder gar nicht verwendet. – GeorgiF

Verwandte Themen