Самый эффективный способ определить, если длина строки! = 0? - PullRequest
10 голосов
/ 02 августа 2010

Я пытаюсь ускорить следующее:

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

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

Проблема в том, что, по-видимому, длина .L подсчитывает символы в строке, и это намного больше работы, чем мне нужно. У кого-нибудь есть идеи, как это ускорить?

Или, есть ли способ определить, существует ли s [0], без проверки оставшейся части строки?

Ответы [ 9 ]

23 голосов
/ 02 августа 2010

РЕДАКТИРОВАТЬ: Теперь, когда вы предоставили больше контекста:

  • Пытаясь воспроизвести это, я вообще не смог найти узкое место в string.Length. Единственный способ сделать это быстрее - это закомментировать и тест , и текст блока if , что не совсем справедливо. Простое комментирование условия замедлило процесс, то есть, безусловно, копирование ссылки было медленнее, чем проверка условия.

  • Как уже указывалось, использование перегрузки string.Split, которая удаляет пустые записи для вас, является настоящей убийственной оптимизацией.

  • Вы можете пойти дальше, избегая создания нового массива char с пробелом в каждый раз. Вы всегда будете эффективно проходить одно и то же, так почему бы не воспользоваться этим?

  • Пустые массивы эффективно неизменяемы. Вы можете оптимизировать нулевой / пустой регистр, всегда возвращая одно и то же.

Оптимизированный код становится:

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 абсолютно не считает буквы в строке. Значение хранится в виде поля - хотя я, кажется, помню, что верхний бит этого поля используется для запоминания того, все ли символы являются ASCII (или использовались в любом случае) для включения других оптимизаций. Таким образом, для доступа к свойству может потребоваться битовая маска, но она все равно будет иметь значение O (1), и я ожидаю, что JIT также ее встроит. (Он реализован как extern, но, надеюсь, это не повлияет на JIT в этом случае - я подозреваю, что это достаточно распространенная операция, чтобы потенциально иметь специальную поддержку.)

Если вы уже знаете, что строка не равна нулю, тогда ваш существующий тест

if (s.Length != 0)

- лучший способ, если вы ищете необработанную производительность IMO. Лично в большинстве случаев я бы написал:

if (s != "")

, чтобы прояснить, что нас интересует не столько длина, сколько значение, как пустая строка. Это будет немного медленнее, чем проверка длины, но я считаю, что это более понятно. Как и всегда, я бы пошел к самому ясному коду, пока у вас не появятся данные тестов / профилирования, чтобы указать, что это действительно - узкое место. Я знаю, что ваш вопрос явно о поиске наиболее эффективного теста, но я все равно упомянул об этом. У вас есть доказательства того, что это является узким местом?

РЕДАКТИРОВАТЬ: просто, чтобы привести более ясные причины для моего предложения , а не с использованием string.IsNullOrEmpty: вызов этого метода подсказывает мне, что вызывающая сторона явно пытается разобраться со случаем, когда переменная имеет значение null иначе они бы не упомянули об этом. Если в этой точке кода это считается ошибкой, если переменная равна null, то вам не следует пытаться обрабатывать ее как обычный случай.

В этой ситуации проверка Length на самом деле лучше в одном смысле, чем предложенный мной тест на неравенство: он действует как неявное утверждение, что переменная не равна нулю. Если у вас есть ошибка, и она равна null, тест выдаст исключение, и ошибка будет обнаружена раньше. Если вы используете тест на равенство, он будет обрабатывать null как отличающийся от пустой строки, поэтому он войдет в тело вашего оператора if. Если вы используете string.IsNullOrEmpty, он будет считаться нулевым, как пустой, поэтому он не войдет в блок.

9 голосов
/ 02 августа 2010

String.IsNullOrEmpty - предпочтительный метод проверки строк нулевой или нулевой длины.

Внутренне он будет использовать длину. Однако свойство Length для строки не должно рассчитываться на лету.

Если вы абсолютно уверены, что строка никогда не будет нулевой, и у вас есть серьезные возражения против String.IsNullOrEmpty, наиболее эффективным кодом, о котором я могу подумать, будет:

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

Или, возможно, даже лучше:

if(s != "")
{
    // Do Something
}
5 голосов
/ 02 августа 2010

Доступ к свойству Length не должен вести подсчет - строки .NET хранят счет внутри объекта.

Исходный код SSCLI / Rotor содержит интересный комментарий, которыйпредполагает, что String.Length является (а) эффективным и (б) магическим:

// 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;
}
3 голосов
/ 02 августа 2010

Вот функция String.IsNullOrEmpty -

if (!String.IsNullOrEmpty(yourstring))
{
  // your code
}
1 голос
/ 03 августа 2010

Исходя из ваших намерений, описанных в вашем ответе, почему бы вам просто не попробовать использовать эту встроенную опцию в Split:

s.Split(new[]{" "}, StringSplitOptions.RemoveEmptyEntries);
0 голосов
/ 13 ноября 2018
String.IsNullOrWhiteSpace(s);

true, если s равно нулю или пусто, или если s состоит исключительно из пробелов.

0 голосов
/ 03 августа 2010

Просто используйте String.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries), и он все сделает за вас.

0 голосов
/ 02 августа 2010
        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));
        }

С помощью секундомера s.length! = 0 занимает меньше тиков, чем s == String.Empty

после того, как я исправлю код

0 голосов
/ 02 августа 2010

Как всегда с производительностью: эталон.
Используя C # 3.5 или более раннюю версию, вы захотите протестировать yourString.Length против String.IsNullOrEmpty(yourString)

используя C # 4, выполните оба вышеупомянутых действия и добавьте String.IsNullOrWhiteSpace(yourString)

Конечно, если вы знаете, что ваша строка никогда не будет пустой, вы можете просто попытаться получить доступ к s[0] и обработать исключение, когда его там нет. Это не обычно хорошая практика, но это может быть ближе к тому, что вам нужно (если s всегда должно иметь непустое значение).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...