Я заметил, что
string1.Length == string2.Length && string1 == string2
на больших строках немного быстрее, чем просто
string1 == string2
Это правда? И хорошо ли сравнивать большие длины строк перед сравнением фактических строк?
Я заметил, что
string1.Length == string2.Length && string1 == string2
на больших строках немного быстрее, чем просто
string1 == string2
Это правда? И хорошо ли сравнивать большие длины строк перед сравнением фактических строк?
string
равен проверке длины перед сравнением символов. Таким образом, вы не сохраняете сравнение содержимого с этим трюком. Вы все равно можете сохранить несколько циклов процессора, потому что ваша проверка длины предполагает, что строки не являются нулевыми, а BCL должен проверить это. Поэтому, если длины не равны в большинстве случаев, вы коротко замыкаете несколько инструкций.
Возможно, я просто ошибаюсь. Может быть, оператор встраивается, и проверки оптимизированы. Кто знает наверняка? (Тот, кто измеряет.)
Если вы заботитесь о сохранении каждого цикла, вы можете, вероятно, использовать, в первую очередь, другую стратегию. Возможно, управляемый код даже не является правильным выбором. Учитывая это, я рекомендую использовать более короткую форму и не использовать дополнительную проверку.
Оператор String.Equality или ==
внутренне вызывает string.Equals
, поэтому используйте string.Equals
или ==
, предоставленных каркасом. Он уже оптимизирован.
Сначала сравниваются ссылки, затем длина, а затем фактические символы.
Вы можете найти исходный код здесь
// Determines whether two strings match.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override bool Equals(Object obj) {
if (this == null) //this is necessary to guard against reverse-pinvokes and
throw new NullReferenceException(); //other callers who do not use the callvirt instruction
String str = obj as String;
if (str == null)
return false;
if (Object.ReferenceEquals(this, obj))
return true;
return EqualsHelper(this, str);
}
и
[System.Security.SecuritySafeCritical] // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private unsafe static bool EqualsHelper(String strA, String strB)
{
Contract.Requires(strA != null);
Contract.Requires(strB != null);
int length = strA.Length;
if (length != strB.Length) return false;
fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
{
char* a = ap;
char* b = bp;
// unroll the loop
#if AMD64
// for AMD64 bit platform we unroll by 12 and
// check 3 qword at a time. This is less code
// than the 32 bit case and is shorter
// pathlength
while (length >= 12)
{
if (*(long*)a != *(long*)b) break;
if (*(long*)(a+4) != *(long*)(b+4)) break;
if (*(long*)(a+8) != *(long*)(b+8)) break;
a += 12; b += 12; length -= 12;
}
#else
while (length >= 10)
{
if (*(int*)a != *(int*)b) break;
if (*(int*)(a+2) != *(int*)(b+2)) break;
if (*(int*)(a+4) != *(int*)(b+4)) break;
if (*(int*)(a+6) != *(int*)(b+6)) break;
if (*(int*)(a+8) != *(int*)(b+8)) break;
a += 10; b += 10; length -= 10;
}
#endif
// This depends on the fact that the String objects are
// always zero terminated and that the terminating zero is not included
// in the length. For odd string sizes, the last compare will include
// the zero terminator.
while (length > 0)
{
if (*(int*)a != *(int*)b) break;
a += 2; b += 2; length -= 2;
}
return (length <= 0);
}
}
Результаты моих тестов
Сравнить 10000 строк до 10000 других строк одинаковой длины (256)
Время (s1 == s2): 32536889 тиков
Время (s1.Length == s2.Length) && & & (s1 == s2): 37380529 тиков
Сравнить 10000 строк до 10000 других строк Случайная длина макс 256
Время (s1 == s2): 27223517 ticks
Время (s1.Length == s2.Length) && & & (s1 == s2): 23419529 тиков
Сравнить 10000 строк до 10000 других строк Случайная длина min 256 max 512
Время (s1 == s2): 28904898 тиков
Время (s1.Length == s2.Length) && & & (s1 == s2): 25442710 тиков
То, что я считаю ошеломляющим, состоит в том, что сравнение 10000 строк равной длины займет больше времени, чем сравнение того же объема данных, который больше.
Все эти тесты были выполнены с точно такими же данными.
Согласно ILSpy, оператор строки ==
определяется как:
public static bool operator ==(string a, string b)
{
return string.Equals(a, b);
}
Что определяется как
public static bool Equals(string a, string b)
{
return a == b || (a != null && b != null && a.Length == b.Length && string.EqualsHelper(a, b));
}
Я предполагаю, что первая a == b
на самом деле является контрольной проверкой равенства (ILSpy просто показывает ее как ==
), иначе это будет бесконечно рекурсивный метод.
Это означает, что ==
уже проверяет длины строк перед фактическим сравнением своих символов.
В завершенных строках имеет смысл просто начинать сравнивать символы, так как вы не можете рассчитать длину строк без итерации всех символов в любом случае, и сравнение, вероятно, приведет к скорейшему завершению.
С подсчитанными по длине строками, сначала следует выполнить сравнение длины, , если вы проверяете байт-правильное равенство. Вы даже не можете начать доступ к символьным данным без извлечения длины, так как может быть нулевой длины.
Если вы делаете реляционное сравнение, знание разных значений не говорит вам, должен ли результат быть положительным или отрицательным. И в сопоставлении с культурой одинаковые строки не подразумевают равную длину. Поэтому для обоих из них вам нужно просто сравнить данные.
Если operator==(string, string)
просто делегирует реляционное сравнение, вы не ожидаете, что сравниваете длины. Поэтому проверка длины перед проведением сравнения может быть выгодной. Но похоже, что Framework начинается с проверки длины.
Для geeks среди нас здесь страница, которая отлично справляется с бенчмаркингом многочисленных способов сравнения строк.
В двух словах самым быстрым методом является CompareOrdinal:
if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}
Вторым лучшим способом, по-видимому, является либо словарь, либо Hashset с "ключом" в качестве строки, которую вы хотите сравнить.
Делает интересное чтение.
Я бы сказал, что первый из них быстрее, а результат string1.Length == string2.Length
- false. Благодаря оценке коротких замыканий (SCE) фактическое сравнение строк не производится, что может сэкономить ваше время.
Если строки равны, то первый из них медленнее, так как сначала проверяет длину, а затем выполняет то же самое, что и второе.
См. http://msdn.microsoft.com/en-us/library/2a723cdk.aspx для информации о &&
и SCE.
Итак, как я и обещал, я написал короткий код с секундомером - вы можете скопировать его и попробовать разные строки и увидеть различия
class Program
{
static void Main(string[] args)
{
string str1 = "put the first value";
string str2 = "put the second value";
CompareTwoStringsWithStopWatch(str1, str2); //Print the results.
}
private static void CompareTwoStringsWithStopWatch(string str1, string str2)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 99999999; i++)
{
if (str1.Length == str2.Length && str1 == str2)
{
SomeOperation();
}
}
stopwatch.Stop();
Console.WriteLine("{0}. Time: {1}", "Result for: str1.Length == str2.Length && str1 == str2", stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < 99999999; i++)
{
if (str1 == str2)
{
SomeOperation();
}
}
stopwatch.Stop();
Console.WriteLine("{0}. Time: {1}", "Result for: str1 == str2", stopwatch.Elapsed);
}
private static int SomeOperation()
{
var value = 500;
value += 5;
return value - 300;
}
}
Мои выводы:
Если вы ожидаете, что строки будут отличаться по длине в большинстве случаев, вы можете сравнить их длины И, а затем сравнить сами строки, используя string.Compare
. Сделав это:
if (str1.Length == str2.Length)
{
if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
{
doSomething()
}
}
В этом случае я ожидаю, что строки будут отличаться почти все время, я думаю, str1.Lenght намного дешевле, чем сравнение фактических строк. Если они равны по размеру, я их сравниваю.
ИЗМЕНИТЬ: Забудьте, что я сказал. Просто используйте ==
и будьте счастливы.