2011-01-18 6 views
6

Quale sarebbe un modo efficiente e portatile per convertire un corto senza segno in un carattere * (ad esempio, converti 25 in "25").Converti in modo efficiente un corto senza segno in un carattere *

Mi piacerebbe evitare cose come ottenere stringhe (std :: string) coinvolte. Le prestazioni sono importanti in questo caso poiché questa conversione dovrà avvenire rapidamente e spesso.

Stavo osservando cose come usare sprintf ma vorrei esplorare tutte le idee.

+3

Hai provato a usare ' sprintf'/'snprintf'? Fatto ciò, hai profilato il codice e stabilito che si tratta di un hotspot di prestazioni? –

+15

Le tabelle nella parte inferiore dell'articolo collegato sotto illustrano chiaramente dove si trovano le implementazioni di stdlib per quanto riguarda l'efficienza e l'ottimalità di implementazione: http://www.codeproject.com/KB/recipes/Tokenizer.aspx –

risposta

6

Prima di tutto, fallo bene, quindi fallo velocemente, ma ottimizza solo se puoi vedere con certezza che un pezzo di codice non è performante.

snprintf() in un buffer farà quello che vuoi. È la soluzione più veloce possibile? Affatto. Ma è tra i più semplici, e basterà rendere il tuo codice operativo. Da lì, se vedi che quelle chiamate a snprintf() sono così laboriose da dover essere ottimizzate, allora e solo dopo cerca una soluzione più veloce.

1

Direi almeno di provare sprintf e dato che hai questo tag come C++, prova StringStream e in realtà li profili. In molti casi il compilatore è abbastanza intelligente da creare qualcosa che funzioni abbastanza bene. Solo quando sai che sarà un collo di bottiglia hai bisogno di trovare un modo più veloce.

2

Un array di stringhe in modo tale che

array[25] = "25"; 
array[26] = "26"; 

array[255] = "255"; 

forse? Potresti scrivere un piccolo programma che genera il codice sorgente della tabella per te abbastanza facilmente, e quindi utilizzare questo file nel tuo progetto.

Modifica: non capisco cosa intendi per non voler utilizzare le stringhe.

+0

Penso che per le stringhe significano "std :: string" s. Anche a me ero confuso. – gravitron

1

provare questo:

int convert(unsigned short val, char* dest) 
{ 
    int i = 0; 
    if (val > 10000) 
    { 
    dest[i++] = (val/10000) | 0x30; 
    val %= 10000; 
    } 
    if (val > 1000) 
    { 
    dest[i++] = (val/1000) | 0x30; 
    val %= 1000; 
    } 
    if (val > 100) 
    { 
    dest[i++] = (val/100) | 0x30; 
    val %= 100; 
    } 
    if (val > 10) 
    { 
    dest[i++] = (val/10) | 0x30; 
    val %= 10; 
    } 
    dest[i++] = (val) | 0x30; 
    dest[i] = 0; 
    return i; 
} 
+1

forse un finale 'dest [i] = '\ 0';'? –

+0

@ Tony, oops: buona cattura! – Nim

1

ho messo insieme una prova di varie funzioni qui, e questo è ciò che mi si avvicinò con:

write_ushort: 7.81 s
uShortToStr: 8.16 s
convert : 6,71 s
use_sprintf: 49,66 s

(Write_ushort è la mia versione, che ho provato a scrivere chiaramente come p ossibile, piuttosto che micro-ottimizzare, da formattare in un determinato buffer di caratteri; use_sprintf è l'ovvio sprintf (buf, "% d", x) e nient'altro; gli altri due sono presi da altre risposte qui.)

Questa è una differenza piuttosto sorprendente tra loro, non è vero? Chi avrebbe mai pensato di usare lo sprint con una differenza di quasi un ordine di grandezza? Oh, sì, quante volte ho ripetuto ogni funzione testata?

// Taken directly from my hacked up test, but should be clear. 
// Compiled with gcc 4.4.3 and -O2. This test is interesting, but not authoritative. 
int main() { 
    using namespace std; 
    char buf[100]; 

#define G2(NAME,STMT) \ 
    { \ 
    clock_t begin = clock(); \ 
    for (int count = 0; count < 3000; ++count) { \ 
     for (unsigned x = 0; x <= USHRT_MAX; ++x) { \ 
     NAME(x, buf, sizeof buf); \ 
     } \ 
    } \ 
    clock_t end = clock(); \ 
    STMT \ 
    } 
#define G(NAME) G2(NAME,) G2(NAME,cout << #NAME ": " << double(end - begin)/CLOCKS_PER_SEC << " s\n";) 
    G(write_ushort) 
    G(uShortToStr) 
    G(convert) 
    G(use_sprintf) 
#undef G 
#undef G2 

    return 0; 
} 

sprintf convertito l'tutta la gamma possibile di pantaloncini firmati, poi ha fatto ancora una volta l'intera gamma 2.999 volte più a circa 0,25 microsecondo per la conversione, in media, sul mio ~ 5 anni portatile.

Sprintf è portatile; è anche abbastanza efficiente per le tue esigenze?


La mia versione:

// Returns number of non-null bytes written, or would be written. 
// If ret is null, does not write anything; otherwise retlen is the length of 
// ret, and must include space for the number plus a terminating null. 
int write_ushort(unsigned short x, char *ret, int retlen) { 
    assert(!ret || retlen >= 1); 

    char s[uint_width_10<USHRT_MAX>::value]; // easy implementation agnosticism 
    char *n = s; 
    if (x == 0) { 
    *n++ = '0'; 
    } 
    else while (x != 0) { 
    *n++ = '0' + x % 10; 
    x /= 10; 
    } 

    int const digits = n - s; 
    if (ret) { 
    // not needed by checking retlen and only writing to available space 
    //assert(retlen >= digits + 1); 

    while (--retlen && n != s) { 
     *ret++ = *--n; 
    } 
    *ret = '\0'; 
    } 
    return digits; 
} 

fase di compilazione log funzioni TMP non sono una novità, ma tra cui questo esempio completo, perché è quello che ho usato:

template<unsigned N> 
struct uint_width_10_nonzero { 
    enum { value = uint_width_10_nonzero<N/10>::value + 1 }; 
}; 
template<> 
struct uint_width_10_nonzero<0> { 
    enum { value = 0 }; 
}; 
template<unsigned N> 
struct uint_width_10 { 
    enum { value = uint_width_10_nonzero<N>::value }; 
}; 
template<> 
struct uint_width_10<0> { 
    enum { value = 1 }; 
}; 
+0

In realtà la differenza non è sorprendente, sprintf ha una grande varietà di specificatori di formato ... possibile esistenza di cui deve tener conto nel suo algoritmo. Non ho mai visto sprintf o anche una bestia più lenta come boost :: format prendendo una percentuale significativa del tempo di esecuzione di profilo, quindi nella maggior parte dei casi non vale la pena di essere ottimizzato. –

+0

Un piccolo angelo mi ha detto di non usare il sarcasmo in questo post e che qualcuno avrebbe commentato. Non ho ascoltato Sembra solo un'enorme differenza perché le funzioni vengono eseguite quasi 200 milioni di volte (USHRT_MAX, qui 65.535, * 3.000). @ ÖöTiib: Hai riaffermato la conclusione che ho mostrato l'OP senza usare un profiler, dato che non ho accesso al codice dell'OP per delinearlo. –

+0

Sì, ma ho anche fornito una soluzione, perché ... chi lo sa. Non può essere sarcastico se non vedi il vero problema. Per qualche motivo le interfacce e i protocolli basati su testo sono diventati popolari e se due moduli parlano tra loro usando sprintf pesantemente per raccontare milioni di cortometraggi non firmati l'un l'altro, allora potrebbe diventare un problema. Preferirei naturalmente passare all'interfaccia/protocollo binario in questo caso, ma riconsiderare le interfacce potrebbe essere costoso/fuori questione per altri motivi. –