std::string_view
ha llegado a C ++ 17 y se recomienda ampliamente usarlo en lugar de const std::string&
.
Una de las razones es el rendimiento.
¿Alguien puede explicar qué tan exactamente std::string_view
es / será más rápido que const std::string&
cuando se usa como un tipo de parámetro? (supongamos que no se hacen copias en la persona que llama)
c++
string
c++17
string-view
Patryk
fuente
fuente
std::string_view
es solo una abstracción del par (char * begin, char * end). Lo usa al hacer unastd::string
copia innecesaria.std::string
(string_view puede aceptar matrices sin procesar, vectores,std::basic_string<>
con asignadores no predeterminados, etc., etc., etc. Ah, y otras string_views obviamente)Respuestas:
std::string_view
Es más rápido en algunos casos.Primero,
std::string const&
requiere que los datos estén en unastd::string
matriz C sin procesar, unachar const*
devuelta por una API C,std::vector<char>
producida por algún motor de deserialización, etc. La conversión de formato evitada evita copiar bytes y (si la cadena es más larga que SBO¹ para lastd::string
implementación particular ) evita una asignación de memoria.No se realizan asignaciones en el
string_view
caso, pero las habría si sefoo
tomara una enstd::string const&
lugar de unastring_view
.La segunda razón realmente importante es que permite trabajar con subcadenas sin una copia. Supongamos que está analizando una cadena json de 2 gigabytes (!) ². Si lo analiza
std::string
, cada uno de estos nodos de análisis donde almacenan el nombre o el valor de un nodo copia los datos originales de la cadena de 2 gb a un nodo local.En cambio, si lo analiza en
std::string_view
s, los nodos hacen referencia a los datos originales. Esto puede ahorrar millones de asignaciones y reducir a la mitad los requisitos de memoria durante el análisis.La aceleración que puedes obtener es simplemente ridícula.
Este es un caso extremo, pero otros casos de "obtener una subcadena y trabajar con él" también pueden generar aceleraciones decentes
string_view
.Una parte importante de la decisión es lo que pierde con el uso
std::string_view
. No es mucho, pero es algo.Pierdes la terminación nula implícita, y eso es todo. Entonces, si la misma cadena se pasará a 3 funciones, todas las cuales requieren un terminador nulo, la conversión a
std::string
una vez puede ser sabio. Por lo tanto, si se sabe que su código necesita un terminador nulo, y no espera cadenas alimentadas desde buffers de fuente de estilo C o similares, tal vez tome unstd::string const&
. De lo contrario, tome unstd::string_view
.Si
std::string_view
tuviera una bandera que indicara si fue anulada (o algo más elegante), eliminaría incluso esa última razón para usar astd::string const&
.Hay un caso donde tomar un
std::string
con noconst&
es óptimo sobre unstd::string_view
. Si necesita poseer una copia de la cadena de forma indefinida después de la llamada, la toma por valor es eficiente. Usted estará en el caso de SBO (y sin asignaciones, solo unas pocas copias de caracteres para duplicarlo), o podrá mover el búfer asignado por el montón a un localstd::string
. Tener dos sobrecargasstd::string&&
ystd::string_view
podría ser más rápido, pero solo marginalmente, y causaría una modesta hinchazón de código (lo que podría costarle todas las ganancias de velocidad).¹ Optimización de búfer pequeño
² Caso de uso real.
fuente
Una forma en que string_view mejora el rendimiento es que permite eliminar prefijos y sufijos fácilmente. Debajo del capó, string_view puede simplemente agregar el tamaño del prefijo a un puntero a algún búfer de cadena, o restar el tamaño del sufijo del contador de bytes, esto generalmente es rápido. std :: string por otro lado tiene que copiar sus bytes cuando haces algo como substr (de esta manera obtienes una nueva cadena que posee su búfer, pero en muchos casos solo quieres obtener parte de la cadena original sin copiar). Ejemplo:
Con std :: string_view:
Actualizar:
Escribí un punto de referencia muy simple para agregar algunos números reales. Utilicé la impresionante biblioteca de Google benchmark . Las funciones comparadas son:
Resultados
(x86_64 linux, gcc 6.2, "
-O3 -DNDEBUG
"):fuente
Hay 2 razones principales:
string_view
es un segmento en un búfer existente, no requiere una asignación de memoriastring_view
se pasa por valor, no por referenciaLas ventajas de tener un corte son múltiples:
char const*
ochar[]
sin asignar un nuevo bufferMejor y más consistente rendimiento en todo.
Pasar por valor también tiene ventajas sobre pasar por referencia, porque el aliasing.
Específicamente, cuando tienes un
std::string const&
parámetro, no hay garantía de que la cadena de referencia no se modifique. Como resultado, el compilador debe volver a buscar el contenido de la cadena después de cada llamada en un método opaco (puntero a datos, longitud, ...).Por otro lado, al pasar un
string_view
valor por, el compilador puede determinar estáticamente que ningún otro código puede modificar la longitud y los punteros de datos ahora en la pila (o en los registros). Como resultado, puede "almacenarlos en caché" en llamadas a funciones.fuente
Una cosa que puede hacer es evitar construir un
std::string
objeto en el caso de una conversión implícita de una cadena terminada en nulo:fuente
const std::string str{"goodbye!"}; foo(str);
probablemente no será más rápido con string_view que con string &string_view
será lento ya que tiene que copiar dos punteros en lugar de un punteroconst string&
?std::string_view
es básicamente un envoltorio alrededor de aconst char*
. Y pasarconst char*
significa que habrá un puntero menos en el sistema en comparación con pasarconst string*
(oconst string&
), porquestring*
implica algo como:Claramente con el propósito de pasar argumentos constantes, el primer puntero es superfluo.
ps Una diferencia sustancial entre
std::string_view
yconst char*
, sin embargo, es que no se requiere que las vistas_cadena estén terminadas en nulo (tienen un tamaño incorporado), y esto permite el empalme aleatorio en el lugar de cadenas más largas.fuente
std::string_view
s son simplemente elegantesconst char*
, punto. GCC los implementa así:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
. Y ese diagrama es ininteligible. @ n.caillou: su propio comentario ya es más preciso que la respuesta. Eso hacestring_view
más que "fantasíachar const*
": es realmente bastante obvio.std::string const*
ystd::string const&
son lo mismo, ¿no?