Che cosa è acronimo SSO nel contesto di std :: string


155

In una domanda C ++ sull'ottimizzazione e lo stile del codice , diverse risposte hanno fatto riferimento a "SSO" nel contesto dell'ottimizzazione delle copie di std::string. Cosa significa SSO in quel contesto?

Chiaramente non "single sign on". "Ottimizzazione della stringa condivisa", forse?


57
Questo è solo un duplicato allo stesso modo in cui "ciò che è 2 + 2" è un duplicato di "qual è il risultato di 200/50". La risposta è la stessa La domanda è completamente diversa. "Chiudi come duplicato" deve essere utilizzato quando più persone fanno la stessa * domanda. Quando una persona chiede "come viene std::stringimplementata" e un'altra chiede "cosa significa SSO", devi essere assolutamente pazzo per considerarli come la stessa domanda
jalf

1
@jalf: se esiste un Q + A esistente che comprende esattamente lo scopo di questa domanda, lo considererei un duplicato (non sto dicendo che l'OP avrebbe dovuto cercarlo da solo, semplicemente che qualsiasi risposta qui coprirà il terreno che è già coperto.)
Oliver Charlesworth,

47
Stai effettivamente dicendo all'OP che "la tua domanda è sbagliata. Ma dovevi conoscere la risposta per sapere cosa avresti dovuto fare". Bel modo di spegnere le persone SO. Inoltre rende inutilmente difficile trovare le informazioni di cui hai bisogno. Se le persone non fanno domande (e la chiusura sta effettivamente dicendo "questa domanda non avrebbe dovuto essere posta"), allora non ci sarebbe modo per le persone che non conoscono già la risposta, di ottenere la risposta a questa domanda
jalf

7
@jalf: Niente affatto. IMO, "vota per chiudere" non implica "cattiva domanda". Uso i voti negativi per questo. Lo considero un duplicato nel senso che tutte le miriadi di domande (i = i ++, ecc.) La cui risposta è "comportamento indefinito" sono duplicate l'una dell'altra. In una nota diversa, perché nessuno ha risposto alla domanda se non è un duplicato?
Oliver Charlesworth,

5
@jalf: sono d'accordo con Oli, la domanda non è un duplicato, ma la risposta sarebbe, quindi reindirizzare a un'altra domanda in cui le risposte già sembrano appropriate. Le domande chiuse come duplicati non scompaiono, ma fungono da puntatori verso un'altra domanda in cui si trova la risposta. La prossima persona che cerca SSO finirà qui, seguirà il reindirizzamento e troverà la sua risposta.
Matthieu M.

Risposte:


213

Sfondo / Panoramica

Le operazioni su variabili automatiche ("dallo stack", che sono variabili create senza chiamare malloc/ new) sono generalmente molto più veloci di quelle che coinvolgono l'archivio gratuito ("l'heap", che sono variabili che vengono create utilizzando new). Tuttavia, la dimensione delle matrici automatiche viene fissata al momento della compilazione, ma non la dimensione delle matrici dal negozio gratuito. Inoltre, le dimensioni dello stack sono limitate (in genere alcuni MiB), mentre l'archivio gratuito è limitato solo dalla memoria del sistema.

SSO è l'ottimizzazione di stringhe corte / piccole. A in std::stringgenere memorizza la stringa come puntatore al negozio gratuito ("l'heap"), che offre caratteristiche di prestazione simili a quelle che si dovrebbero chiamare new char [size]. Ciò impedisce un overflow dello stack per stringhe molto grandi, ma può essere più lento, soprattutto con le operazioni di copia. Come ottimizzazione, molte implementazioni di std::stringcreano un piccolo array automatico, qualcosa del genere char [20]. Se hai una stringa di 20 caratteri o più piccola (dato questo esempio, la dimensione effettiva varia), la memorizza direttamente in quella matrice. Questo evita la necessità di chiamare new, accelerando un po 'le cose.

MODIFICARE:

Non mi aspettavo che questa risposta fosse così popolare, ma dato che lo è, lasciami dare un'implementazione più realistica, con l'avvertenza che in realtà non ho mai letto nessuna implementazione di SSO "in the wild".

Dettagli di implementazione

Come minimo, è std::stringnecessario memorizzare le seguenti informazioni:

  • La dimensione
  • La capacità
  • La posizione dei dati

La dimensione può essere memorizzata come std::string::size_typeo come puntatore alla fine. L'unica differenza è se si desidera sottrarre due puntatori quando l'utente chiama sizeo aggiungere size_typea un puntatore quando l'utente chiama end. La capacità può essere archiviata in entrambi i modi.

Non paghi per ciò che non usi.

Innanzitutto, considera l'implementazione ingenua in base a ciò che ho descritto sopra:

class string {
public:
    // all 83 member functions
private:
    std::unique_ptr<char[]> m_data;
    size_type m_size;
    size_type m_capacity;
    std::array<char, 16> m_sso;
};

Per un sistema a 64 bit, ciò significa generalmente che std::stringha 24 byte di "overhead" per stringa, più altri 16 per il buffer SSO (16 scelti qui invece di 20 a causa dei requisiti di riempimento). Non avrebbe davvero senso archiviare quei tre membri di dati più una matrice locale di caratteri, come nel mio esempio semplificato. Se m_size <= 16, allora inserirò tutti i dati m_sso, quindi conosco già la capacità e non ho bisogno del puntatore ai dati. Se m_size > 16non ho bisogno m_sso. Non c'è assolutamente alcuna sovrapposizione dove ho bisogno di tutti loro. Una soluzione più intelligente che non spreca spazio avrebbe un aspetto un po 'più simile a questo (non testato, solo a scopo di esempio):

class string {
public:
    // all 83 member functions
private:
    size_type m_size;
    union {
        class {
            // This is probably better designed as an array-like class
            std::unique_ptr<char[]> m_data;
            size_type m_capacity;
        } m_large;
        std::array<char, sizeof(m_large)> m_small;
    };
};

Suppongo che la maggior parte delle implementazioni sia più simile a questa.


7
Ecco una buona spiegazione di alcune implementazioni attuali: stackoverflow.com/a/28003328/203044
BillT

SSO è davvero pratico quando la maggior parte degli sviluppatori passa std :: string usando riferimenti const?
Gupta,

1
SSO ha due vantaggi oltre a rendere la copia più economica. Il primo è che se la dimensione della stringa si adatta alla dimensione del buffer piccola, non è necessario allocare sulla costruzione iniziale. Il secondo è che quando una funzione accetta un std::string const &, arrivare ai dati è una singola indiretta di memoria, perché i dati sono memorizzati nella posizione del riferimento. Se non ci fosse l'ottimizzazione della stringa di piccole dimensioni, l'accesso ai dati richiederebbe due indirette di memoria (prima per caricare il riferimento alla stringa e leggere il suo contenuto, quindi la seconda per leggere il contenuto del puntatore di dati nella stringa).
David Stone,

34

SSO è l'abbreviazione di "Small String Optimization", una tecnica in cui le stringhe di piccole dimensioni sono incorporate nel corpo della classe di stringhe anziché utilizzare un buffer allocato separatamente.


15

Come già spiegato dalle altre risposte, SSO significa ottimizzazione di stringhe piccole / corte . La motivazione alla base di questa ottimizzazione è la prova innegabile che le applicazioni in generale gestiscono stringhe molto più brevi rispetto alle stringhe più lunghe.

Come spiegato da David Stone nella sua risposta precedente , la std::stringclasse utilizza un buffer interno per archiviare i contenuti fino a una determinata lunghezza e ciò elimina la necessità di allocare dinamicamente la memoria. Questo rende il codice più efficiente e veloce .

Questa altra risposta correlata mostra chiaramente che la dimensione del buffer interno dipende std::stringdall'implementazione, che varia da piattaforma a piattaforma (vedere i risultati dei benchmark di seguito).

Punti di riferimenti

Ecco un piccolo programma che confronta le operazioni di copia di molte stringhe con la stessa lunghezza. Inizia a stampare il tempo per copiare 10 milioni di stringhe con lunghezza = 1. Quindi si ripete con stringhe di lunghezza = 2. Continua fino a quando la lunghezza è 50.

#include <string>
#include <iostream>
#include <vector>
#include <chrono>

static const char CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const int ARRAY_SIZE = sizeof(CHARS) - 1;

static const int BENCHMARK_SIZE = 10000000;
static const int MAX_STRING_LENGTH = 50;

using time_point = std::chrono::high_resolution_clock::time_point;

void benchmark(std::vector<std::string>& list) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

    // force a copy of each string in the loop iteration
    for (const auto s : list) {
        std::cout << s;
    }

    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    std::cerr << list[0].length() << ',' << duration << '\n';
}

void addRandomString(std::vector<std::string>& list, const int length) {
    std::string s(length, 0);
    for (int i = 0; i < length; ++i) {
        s[i] = CHARS[rand() % ARRAY_SIZE];
    }
    list.push_back(s);
}

int main() {
    std::cerr << "length,time\n";

    for (int length = 1; length <= MAX_STRING_LENGTH; length++) {
        std::vector<std::string> list;
        for (int i = 0; i < BENCHMARK_SIZE; i++) {
            addRandomString(list, length);
        }
        benchmark(list);
    }

    return 0;
}

Se vuoi eseguire questo programma, dovresti farlo in ./a.out > /dev/nullmodo che il tempo di stampa delle stringhe non venga conteggiato. I numeri che contano vengono stampati stderr, quindi verranno visualizzati nella console.

Ho creato grafici con l'output del mio MacBook e delle macchine Ubuntu. Si noti che c'è un enorme salto nel tempo per copiare le stringhe quando la lunghezza raggiunge un determinato punto. Questo è il momento in cui le stringhe non rientrano più nel buffer interno e deve essere utilizzata l'allocazione di memoria.

Nota anche che sulla macchina Linux, il salto si verifica quando la lunghezza della stringa raggiunge 16. Sul macbook, il salto si verifica quando la lunghezza raggiunge 23. Ciò conferma che SSO dipende dall'implementazione della piattaforma.

Ubuntu Benchmark SSO su Ubuntu

Macbook Pro Benchmark SSO su Macbook Pro

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.