Как стать автором
Обновить

Комментарии 11

Дико смотрятся сравнение строк через ToLowerInvariant в статье про перформанс. Надеюсь автор знает что это далеко не самое оптимальное решение и просто забыл это упомянуть.

Я могу ошибаться, но, вроде, StringComparer, который предоставляет сравнение без учета регистра тоже за кулисами делает приведение к одному регистру.
Вот откуда берутся эти «догадки», весь .NET открыт для изучения:

TL;DN
Нет, всё упирается в нативный код платформы


String.Equals
public bool Equals(string value, StringComparison comparisonType)
{
	if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
	{
		throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
	}
	if ((object)this == value)
	{
		return true;
	}
	if (value != null)
	{
		switch (comparisonType)
		{
		case StringComparison.CurrentCulture:
			return CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0;
		case StringComparison.CurrentCultureIgnoreCase:
			return CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0;
		case StringComparison.InvariantCulture:
			return CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0;
		case StringComparison.InvariantCultureIgnoreCase:
			return CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0;
		case StringComparison.Ordinal:
			if (Length != value.Length)
			{
				return false;
			}
			return EqualsHelper(this, value);
		case StringComparison.OrdinalIgnoreCase:
			if (Length != value.Length)
			{
				return false;
			}
			if (IsAscii() && value.IsAscii())
			{
				return EqualsIgnoreCaseAsciiHelper(this, value);
			}
			return TextInfo.CompareOrdinalIgnoreCase(this, value) == 0;
		default:
			throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
		}
	}
	return false;
}


для ordinal ASCII строк:
private unsafe static bool EqualsIgnoreCaseAsciiHelper(string strA, string strB)
{
	int num = strA.Length;
	fixed (char* ptr = &strA.m_firstChar)
	{
		fixed (char* ptr3 = &strB.m_firstChar)
		{
			char* ptr2 = ptr;
			char* ptr4 = ptr3;
			while (true)
			{
				if (num == 0)
				{
					return true;
				}
				int num2 = *ptr2;
				int num3 = *ptr4;
				if (num2 != num3 && ((num2 | 0x20) != (num3 | 0x20) || (uint)((num2 | 0x20) - 97) > 25u))
				{
					break;
				}
				ptr2++;
				ptr4++;
				num--;
			}
			return false;
		}
	}
}


Для остальных строк:

INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase(
    LPCWSTR string1, INT32 index1,
    LPCWSTR string2, INT32 index2,
    INT32 length1,
    INT32 length2)
{
    CONTRACTL
    {
        QCALL_CHECK;
        PRECONDITION(CheckPointer(string1));
        PRECONDITION(CheckPointer(string2));
    } CONTRACTL_END;

    INT32 result = 0;

    BEGIN_QCALL;
    //
    //  Get the arguments.
    //  We assume the caller checked them before calling us
    //

    // We don't allow the -1 that native code allows
    _ASSERT(length1 >= 0);
    _ASSERT(length2 >= 0);

    // Do the comparison
#ifndef FEATURE_CORECLR
    AppDomain* curDomain = GetAppDomain();
    
    if (curDomain->m_pCustomSortLibrary != NULL) {
        result = (curDomain->m_pCustomSortLibrary->pCompareStringOrdinal)(string1 + index1, length1, string2 + index2, length2, TRUE);
    } 
    else 
#endif
    {
        result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE);
    }

    // The native call shouldn't fail
    _ASSERT(result != 0);
    if (result == 0)
    {
        // return value of 0 indicates failure and error value is supposed to be set.
        // shouldn't ever really happen
        _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal!  This is usually due to bad arguments.");
    }

    // Adjust the result to the expected -1, 0, 1 result
    result -= 2;

    END_QCALL;

    return result;
}




который вызывает WinAPI CompareStringOrdinal

Для Invariant culture строк:

INT32 QCALLTYPE COMNlsInfo::InternalCompareString(
    INT_PTR handle,
    INT_PTR handleOrigin,
    LPCWSTR localeName,
    LPCWSTR string1, INT32 offset1, INT32 length1,
    LPCWSTR string2, INT32 offset2, INT32 length2,
    INT32 flags)
{
    CONTRACTL
    {
        QCALL_CHECK;
        PRECONDITION(CheckPointer(string1));
        PRECONDITION(CheckPointer(string2));
        PRECONDITION(CheckPointer(localeName));
    } CONTRACTL_END;

    INT32 result = 1;
    BEGIN_QCALL;

    handle = EnsureValidSortHandle(handle, handleOrigin, localeName);

#ifndef FEATURE_CORECLR
    AppDomain* curDomain = GetAppDomain();

    if(!(curDomain->m_bUseOsSorting))
    {
        result = SortVersioning::SortDllCompareString((SortVersioning::PSORTHANDLE) handle, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, 0);
    }
    else if (curDomain->m_pCustomSortLibrary != NULL) {
        result = (curDomain->m_pCustomSortLibrary->pCompareStringEx)(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, NULL, (LPARAM) handle);
    } 
    else 
#endif
    {
        result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle);
    }

    switch (result)
    {
        case CSTR_LESS_THAN:
            result = -1;
            break;

        case CSTR_EQUAL:
            result = 0;
            break;

        case CSTR_GREATER_THAN:
            result = 1;
            break;

        case 0:
        default:
            _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx!  This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
            break;
    }

    END_QCALL;
    return result;
}


Который заканчивается в WinAPI CompareStringEx

А каким будет самое оптимальное решение?
В кейсе автора
string.Equals(xxx, StringComparison.InvariantCultureIgnoreCase)

Но автор не указал, что хотел именно культурно-инвариантное сравнение (а только то, что хотел более быстрый вариант),
тогда StringComparison.OrdinalIgnoreCase подойдет лучше.

В остальных кейсах надо смотреть. Но для сравнения строк, ToUpper/ToLower всегда будут неправильным решением.
Можно было б еще Sum из LINQ добавить в тест массивов и списков.

Оптимизация это всегда интересно, но о ней имеет смысл говорить только зная конкретный контекст в котором исполняется оптимизируемый код. Автор пишет что в старом проекте валидация полей делается через проброс эксепшена, но разве это плохо? Если это валидация полей формы то это нормульно и получается читабельный код. Если это валидация класса и крутится в цикле на нагруженном сервере и постоянно кидает исключения чтобы выше по стеку их поймать то это беда...


И еще, чем автору не угодил стандартный String.Equals, что понадобились такие извращения с приведением к верхнему регистру с аллокацией новых объектов?

Не очень понятен данный абзац:
Если вы находитесь на должности team-lead, senior, middle или вы руководитель отдела то, должны понимать что быстрая разработка проекта увеличивает ценность команды и данная технология позволит вам экономить время как разработчиков, так и само время работки проекта.

Как это связано со скоростью разработки? Или это описка и имеется ввиду сборка проекта?
Да, и оно все же называется Tiered compilation

С версии 2.2 появилась Tier Compilation, благодаря которой программисты будут тратить меньше времени на запуск проекта.

TC доступен и в 2.1, но его нужно включать. В 2.2 включен TC по умолчанию.
при учёте того что эти два проекта существовали чтобы обрабатывать огромные excel таблицы, результат был более чем значимым для продукта.

Насколько значимым? Чтобы такая мелочь заметно повлияла на реальную жизнь нужен какой-то весьма специфический продукт.

В условиях NDA соглашения я не могу рассказать, что именно разрабатывал, но этот проект используется в огромной бюджетной системе одной страны где выгрузка в Excel и сбор многих данных (приблизительно от 10 до 1000 тысяч), которые нужно привести к общему виду и сравнить по определенным условиям. Т.к. сравнение было обычным делом в каждом из отчётов, прирост был очень значим.

«в огромной бюджетной системе одной страны где выгрузка в Excel и сбор многих данных»

Кривая хрень, короче. Hadoop, Spark, не, не слышали ёксЕль!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории