2010-08-02 14 views
5

Estoy tratando de acelerar el siguiente:La forma más eficiente de determinar si una longitud de cadena es = 0?

string s; //--> s is never null 

if (s.Length != 0) 
{ 
    <do something> 
} 

El problema es que parece que el .length realmente cuenta los caracteres de la cadena, y esto es mucho más trabajo de lo que necesito. ¿Alguien tiene una idea sobre cómo acelerar esto?

O, ¿hay alguna manera de determinar si s [0] existe, sin verificar el resto de la cadena?

+1

¡El problema de perf que enfrenta es muy probable que se resuelva en otro lugar! ¿Hiciste un perfil y descubriste que el cuello de botella realmente está aquí? –

+0

¿Por curiosidad a qué versión del marco está apuntando? –

+0

** NOTA ** para otros posibles contendientes: Eric ha publicado una respuesta que aclara su pregunta (un poco) a continuación ... –

Respuesta

5

El acceso a la propiedad Length no debe hacer un recuento: las cadenas .NET almacenan un recuento dentro del objeto.

El SSCLI/Rotor source code contiene un comentario interesante que sugiere que String.Length es (a) eficiente y (b) Magic:

// Gets the length of this string 
// 
/// This is a EE implemented function so that the JIT can recognise is specially 
/// and eliminate checks on character fetchs in a loop like: 
/// for(int I = 0; I < str.Length; i++) str[i] 
/// The actually code generated for this will be one instruction and will be inlined. 
// 
public extern int Length { 
    [MethodImplAttribute(MethodImplOptions.InternalCall)] 
    get; 
} 
7

String.IsNullOrEmpty es el método preferido para la comprobación de las cadenas de longitud nula o cero.

Internamente, utilizará Longitud. Sin embargo, la propiedad Length para una cadena no se debe calcular sobre la marcha.

Si está completamente seguro de que la cadena nunca será nula y tienes algo fuerte objeción a String.IsNullOrEmpty, el código más eficiente que se me ocurre sería:

if(s.Length > 0) 
{ 
    // Do Something 
} 

O, posiblemente, incluso mejor:

if(s != "") 
{ 
    // Do Something 
} 
+0

Lo suficiente, pero es excesivo si se sabe que la cadena no es nula. –

+0

No solo es excesivo, sino que sugiere que piense que la variable * podría * ser nula. –

+0

luego compáralo con 'String.Empty' – ankitjaininfo

3

Aquí es la función String.IsNullOrEmpty -

if (!String.IsNullOrEmpty(yourstring)) 
{ 
    // your code 
} 
+0

Por favor, explique. – Hut8

+0

@bowenl, ¿por qué votaste? – ankitjaininfo

+0

Porque inicialmente había publicado algo diferente que no compilaba en absoluto. Me retracté ahora que lo has arreglado para que coincida con lo que alguien más publicó antes. – Hut8

20

EDIT: Ahora que usted ha proporcionado un contexto más:

  • tratando de reproducir esto, no pude encontrar un cuello de botella en string.Length en absoluto. La única forma de hacerlo más rápido fue comentar la prueba y el cuerpo del bloque if, lo cual no es realmente justo. El solo hecho de comentar la condición aminoró la velocidad, es decir, la copia incondicional de la referencia fue más lenta que la comprobación de la condición.

  • Como se ha señalado, usar la sobrecarga de string.Split que elimina las entradas vacías para usted es la verdadera optimización extrema.

  • Puede ir más lejos, evitando crear una nueva matriz de caracteres con solo un espacio en todo momento. Siempre pasarás lo mismo de manera efectiva, ¿por qué no aprovechar eso?

  • Los arreglos vacíos son efectivamente inmutables. Puede optimizar el caso nulo/vacío siempre devolviendo lo mismo.

El código optimizado se convierte en:

private static readonly char[] Delimiters = " ".ToCharArray(); 
private static readonly string[] EmptyArray = new string[0]; 

public static string[] SplitOnMultiSpaces(string text) 
{ 
    if (string.IsNullOrEmpty(text)) 
    { 
     return EmptyArray; 
    } 

    return text.Split(Delimiters, StringSplitOptions.RemoveEmptyEntries); 
} 

String.Length no hace absolutamente no contar las cartas en la cadena. El valor se almacena como un campo, aunque parece recordar que el bit superior de ese campo se utiliza para recordar si todos los caracteres son o no ASCII (o solían serlo, de todos modos) para permitir otras optimizaciones. Así que el acceso a la propiedad puede necesitar una máscara de bits, pero seguirá siendo O (1) y yo esperaría que el JIT también lo alinee. (Se implementó como extern, pero con suerte eso no afectaría al JIT en este caso; sospecho que es una operación lo suficientemente común como para tener soporte especial).

Si ya sabe que la cadena no es nula, entonces su prueba existente de

if (s.Length != 0) 

es el mejor camino a seguir si usted está buscando un rendimiento bruto de la OMI. Personalmente en la mayoría de los casos me gustaría escribir:

if (s != "") 

para hacerlo más claro que no estamos tan interesados ​​en la longitud como un valor que si es o no es una cadena vacía. Eso será un poco más lento que la prueba de longitud, pero creo que es más claro. Como siempre, iría por el código más claro hasta que tenga datos de referencia/perfil para indicar que esto realmente es un cuello de botella. Sé que su pregunta es explícitamente sobre encontrar la prueba más eficiente, pero pensé que mencionaría esto de todos modos. ¿Tiene evidencia de que este es un cuello de botella?

EDIT: Sólo para dar razones claras para mi sugerencia de no usando string.IsNullOrEmpty: una llamada a ese método me sugiere que la persona está tratando de forma explícita para tratar el caso en que la variable es nula, de lo contrario wouldn' lo he mencionado. Si en este punto del código cuenta como un error si la variable es nula, entonces no debería tratar de manejarla como un caso normal.

En esta situación, el cheque Length es en realidad mejor de una manera que la prueba de la desigualdad que he sugerido: actúa como una afirmación implícita de que la variable no es nulo. Si tiene un error y es nulo, la prueba lanzará una excepción y el error se detectará temprano. Si usa la prueba de igualdad, considerará nulo como diferente a la cadena vacía, por lo que irá a su cuerpo de declaración "si". Si usa string.IsNullOrEmpty, considerará que null es lo mismo que vacío, por lo que no entrará en el bloque.

+2

Creo que lo mejor de esto es la última línea. – msarchet

+0

@msarchet: desafortunadamente edité la respuesta mientras escribías ese comentario. Supongo que quiere decir "¿Tiene evidencia de que este * es * un cuello de botella?" –

+0

sí, aunque todavía se aplica – msarchet

0

Como siempre con performace: benchmark.
El uso de C# 3.5, o antes, tendrá que poner a prueba yourString.Length vs String.IsNullOrEmpty(yourString)

usando C# 4, hacer ambas cosas de lo anterior y añadir String.IsNullOrWhiteSpace(yourString)

Por supuesto, si usted sabe que su cadena nunca estará vacía, puede intentar acceder al s[0] y manejar la excepción cuando no está allí. Eso no es normalmente buena práctica, pero puede estar más cerca de lo que necesita (si s siempre debe tener un valor no en blanco).

+0

'IsNullOrWhitespace' dará la respuesta incorrecta, dado los requisitos en la pregunta. –

0
 for (int i = 0; i < 100; i++) 
     { 
      System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); 
      string s = "dsfasdfsdafasd"; 

      timer.Start(); 
      if (s.Length > 0) 
      { 
      } 

      timer.Stop(); 
      System.Diagnostics.Debug.Write(String.Format("s.Length != 0 {0} ticks  ", timer.ElapsedTicks)); 

      timer.Reset(); 
      timer.Start(); 
      if (s == String.Empty) 
      { 
      } 

      timer.Stop(); 
      System.Diagnostics.Debug.WriteLine(String.Format("s== String.Empty {0} ticks", timer.ElapsedTicks)); 
     } 

uso del cronómetro del s.length! = 0 tarda menos garrapatas entonces s == String.Empty

después de corregir el código

1

Basado en su intento se describe en su respuesta, ¿por qué qué no se intenta utilizar esta opción incorporada sobre el Split:

s.Split(new[]{" "}, StringSplitOptions.RemoveEmptyEntries); 
0

sólo tiene que utilizar String.split (nuevo char [] {' '}, StringSplitOptions.RemoveEmptyEntries) y lo hará todo por ti.

Cuestiones relacionadas