2011-01-18 10 views
6

¿Cuál sería una forma eficiente y portátil de convertir un corto sin signo en un char * (es decir, convertir 25 en '25').Convierta de manera eficiente un corto sin signo en un char *

Me gustaría evitar cosas como obtener cadenas (std :: string) involucradas. El rendimiento es importante en este caso, ya que esta conversión tendrá que suceder de manera rápida y frecuente.

Estaba buscando cosas como el uso de sprintf, pero me gustaría explorar todas y cada una de las ideas.

+3

¿Ha intentado utilizar ' sprintf'/'snprintf'? Una vez hecho esto, ¿ha perfilado el código y ha determinado que se trata de un punto de acceso de rendimiento? –

+15

Las tablas en la parte inferior del artículo vinculado a continuación ilustran claramente dónde residen las implementaciones stdlib en cuanto a la eficiencia y la optimalidad de la implementación: http://www.codeproject.com/KB/recipes/Tokenizer.aspx –

Respuesta

6

En primer lugar, hazlo bien, luego hazlo rápido, solo optimízalo si puedes ver con certeza que una parte del código no está funcionando.

snprintf() en un búfer hará lo que quiera. ¿Es la solución más rápida posible? De ningún modo. Pero es uno de los más simples, y será suficiente para que su código entre en funcionamiento. Desde allí, si ve que esas llamadas al snprintf() son tan laboriosas que deben optimizarse, entonces y solo entonces busque una solución más rápida.

1

Yo diría que al menos intente con sprintf y ya que tiene esto etiquetado como C++, intente con StringStream, y encuéntrelos. En muchos casos, el compilador es lo suficientemente inteligente como para construir algo que funciona bastante bien. Solo cuando sabes que va a ser un cuello de botella necesitas encontrar una manera más rápida.

2

Una matriz de cadenas de tal manera que

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

array[255] = "255"; 

tal vez? Puede escribir un pequeño programa que genere el código fuente de la tabla para usted con bastante facilidad, y luego usar este archivo en su proyecto.

Editar: No entiendo lo que quieres decir con que no quieres ver las cadenas implicadas.

+0

Creo que por cadenas quieren decir "std :: string" s. Yo también estaba confundido. – gravitron

1

probar esto:

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

quizás un final 'dest [i] = '\ 0';'? –

+0

@Tony, oops - ¡buena captura! – Nim

1

Pirateé juntos una prueba de varias funciones aquí, y esto es lo que ocurrió:

write_ushort: 7,81 s
uShortToStr: 8,16 s
convertido : 6.71 s
use_sprintf: 49.66 s

(Write_ushort es mi versión, que traté de escribir tan claro como p posible, en lugar de micro optimizar, formatear en un búfer de caracteres dado; use_sprintf es el sprintf obvio (buf, "% d", x) y nada más; los otros dos están tomados de otras respuestas aquí.)

Esta es una diferencia bastante sorprendente entre ellos, ¿no? ¿Quién pensaría usar sprintf con casi una diferencia de orden de magnitud? Oh, sí, ¿cuántas veces repetí cada función probada?

// 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 convirtió el toda posible rango de unsignedshorts, luego hizo toda la gama de nuevo 2.999 veces más en alrededor de 0,25 mu s por la conversión, en promedio, en mi ~ 5 años portátil.

Sprintf es portátil; ¿es también lo suficientemente eficiente para tus necesidades?


Mi versión:

// 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; 
} 

tiempo de compilación ingrese funciones TMP no son nada nuevo, pero la inclusión de este ejemplo completo porque es lo que he utilizado:

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

En realidad, la diferencia no es tan sorprendente, sprintf tiene una amplia variedad de especificadores de formato ... posible existencia de los que tiene que tener en cuenta en su algoritmo. Nunca he visto sprintf o incluso alguna bestia más lenta como boost :: format tomando un porcentaje significativo de tiempo de ejecución en el perfil, así que en la mayoría de los casos no vale la pena optimizarlo. –

+0

Un angelito me dijo que no use el sarcasmo en esta publicación y que alguien lo comentaría. No escuché. Solo parece una gran diferencia porque las funciones se ejecutan casi 200 millones de veces (USHRT_MAX, aquí 65,535, * 3,000). @ ÖöTiib: Ha reiterado la conclusión de que mostré el OP sin utilizar un generador de perfiles, ya que no tengo acceso al código del OP para perfilarlo. –

+0

Sí, pero también proporcioné la solución, porque ... quién sabe. No puede ser sarcástico si no ves el verdadero problema. Por alguna razón, las interfaces y protocolos basados ​​en texto se han vuelto populares y si dos módulos se comunican entre sí usando sprintf para contar millones de cortos sin firmar, entonces puede convertirse en cuello de botella. Por supuesto, preferiría cambiar a la interfaz binaria/protocolo en tal caso, pero reconsiderar las interfaces puede ser costoso/estar fuera de dudas por otras razones. –

Cuestiones relacionadas