Приветствую!
Сразу хочется скачать, что речь пойдет исключительно о личном мнении и практике, которая помогла ускорить то, о чем ранее как-то не сильно приходилось задумываться.
Работая над проектом с многочисленными вычислениями и необходимостью приводить результаты к стринг, довелось делать различные замеры и проверки того, что там и как работает.
Благодаря небезызвестному доттрейсу, удалось заметить некоторое «оттягивание» времени на .ToString().
Обычная привычка, наверное, многим знакомая, если вдруг что, пишем:
И лично я не задумывался над тем, что такой подход при многократных выполнениях может быть медлительным.
Понятное дело, что речь не идет о слишком существенных «тормозах», но если можно как-то просто оптимизировать с виду вполне оптимизированный метод, почему бы и да?
Для проверки нескольких гипотез были сделаны следующие методы:
Далее пошли запуски на разных компьютерах и вот какие получены результаты:
Основная суть проблемы производительности при «а напишу как обычно, чего такого?» заключается в мелочах, подсказанных трейсером и кодами от товарищей из Майкрософта.
Если не передавать никаких аргументов, то выполняется неприятное дело (за нас все решают):
В подобной ситуации будут дополнительные накладные расходы на все, на чем только можно и очень хочется избегать подобных ситуаций, ведь оказалось, что это крайне просто – делаем выбор сами, имеем право.
При этом интересна реализация перегрузки:
И вот тут я нашел то, что лично для меня показалось крайне удачным решением и, откровенно говоря, неявным ускорением о котором вообще не было мыслей:
А CultureInfo то проверяется первым, значит, есть вероятность выиграть слегка времени еще и на этом моменте, потому были сделаны те методы, которые были сделаны для тестов.
Для себя я сделал вывод, что выгодней всего делать следующим образом:
Материал не претендует на научность, но позитив от чтения приветствуется.
Сразу хочется скачать, что речь пойдет исключительно о личном мнении и практике, которая помогла ускорить то, о чем ранее как-то не сильно приходилось задумываться.
Работая над проектом с многочисленными вычислениями и необходимостью приводить результаты к стринг, довелось делать различные замеры и проверки того, что там и как работает.
Благодаря небезызвестному доттрейсу, удалось заметить некоторое «оттягивание» времени на .ToString().
Обычная привычка, наверное, многим знакомая, если вдруг что, пишем:
const int i = 424242;
string str = i.ToString();
Console.WriteLine(str);
И лично я не задумывался над тем, что такой подход при многократных выполнениях может быть медлительным.
Понятное дело, что речь не идет о слишком существенных «тормозах», но если можно как-то просто оптимизировать с виду вполне оптимизированный метод, почему бы и да?
Для проверки нескольких гипотез были сделаны следующие методы:
Тестовые методы
public class ToStringTest
{
public const int MaxCount = 1000000;
public string a_1, a_2, a_3, a_4, a_5, a_6, a_7, a_8, a_9, a_10;
[Benchmark(Description = "CultureInfoIn")]
public void CultureInfoIn()
{
for (int i = 0; i < MaxCount; i++)
{
a_1 = i.ToString(CultureInfo.CurrentCulture);
a_2 = i.ToString(CultureInfo.CurrentCulture);
a_3 = i.ToString(CultureInfo.CurrentCulture);
a_4 = i.ToString(CultureInfo.CurrentCulture);
a_5 = i.ToString(CultureInfo.CurrentCulture);
a_6 = i.ToString(CultureInfo.CurrentCulture);
a_7 = i.ToString(CultureInfo.CurrentCulture);
a_8 = i.ToString(CultureInfo.CurrentCulture);
a_9 = i.ToString(CultureInfo.CurrentCulture);
a_10 = i.ToString(CultureInfo.CurrentCulture);
}
}
[Benchmark(Description = "CultureInfoOut")]
public void CultureInfoOut()
{
CultureInfo culture = CultureInfo.CurrentCulture;
for (int i = 0; i < MaxCount; i++)
{
a_1 = i.ToString(culture);
a_2 = i.ToString(culture);
a_3 = i.ToString(culture);
a_4 = i.ToString(culture);
a_5 = i.ToString(culture);
a_6 = i.ToString(culture);
a_7 = i.ToString(culture);
a_8 = i.ToString(culture);
a_9 = i.ToString(culture);
a_10 = i.ToString(culture);
}
}
[Benchmark(Description = "NumberFormatInfoIn")]
public void NumberFormatInfoIn()
{
for (int i = 0; i < MaxCount; i++)
{
a_1 = i.ToString(NumberFormatInfo.CurrentInfo);
a_2 = i.ToString(NumberFormatInfo.CurrentInfo);
a_3 = i.ToString(NumberFormatInfo.CurrentInfo);
a_4 = i.ToString(NumberFormatInfo.CurrentInfo);
a_5 = i.ToString(NumberFormatInfo.CurrentInfo);
a_6 = i.ToString(NumberFormatInfo.CurrentInfo);
a_7 = i.ToString(NumberFormatInfo.CurrentInfo);
a_8 = i.ToString(NumberFormatInfo.CurrentInfo);
a_9 = i.ToString(NumberFormatInfo.CurrentInfo);
a_10 = i.ToString(NumberFormatInfo.CurrentInfo);
}
}
[Benchmark(Description = "NumberFormatInfoOut")]
public void NumberFormatInfoOut()
{
NumberFormatInfo culture = NumberFormatInfo.CurrentInfo;
for (int i = 0; i < MaxCount; i++)
{
a_1 = i.ToString(culture);
a_2 = i.ToString(culture);
a_3 = i.ToString(culture);
a_4 = i.ToString(culture);
a_5 = i.ToString(culture);
a_6 = i.ToString(culture);
a_7 = i.ToString(culture);
a_8 = i.ToString(culture);
a_9 = i.ToString(culture);
a_10 = i.ToString(culture);
}
}
[Benchmark(Description = "ToString")]
public void DefaultToString()
{
for (int i = 0; i < MaxCount; i++)
{
a_1 = i.ToString();
a_2 = i.ToString();
a_3 = i.ToString();
a_4 = i.ToString();
a_5 = i.ToString();
a_6 = i.ToString();
a_7 = i.ToString();
a_8 = i.ToString();
a_9 = i.ToString();
a_10 = i.ToString();
}
}
}
Далее пошли запуски на разных компьютерах и вот какие получены результаты:
Результаты тестов








Основная суть проблемы производительности при «а напишу как обычно, чего такого?» заключается в мелочах, подсказанных трейсером и кодами от товарищей из Майкрософта.
Если не передавать никаких аргументов, то выполняется неприятное дело (за нас все решают):
[System.Security.SecuritySafeCritical]
[Pure]
public override String ToString() {
Contract.Ensures(Contract.Result<String>() != null);
return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo);
}
public static NumberFormatInfo CurrentInfo {
get {
System.Globalization.CultureInfo culture = System.Threading.Thread.CurrentThread.CurrentCulture;
if (!culture.m_isInherited) {
NumberFormatInfo info = culture.numInfo;
if (info != null) {
return info;
}
}
return ((NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo)));
}
}
В подобной ситуации будут дополнительные накладные расходы на все, на чем только можно и очень хочется избегать подобных ситуаций, ведь оказалось, что это крайне просто – делаем выбор сами, имеем право.
При этом интересна реализация перегрузки:
[Pure]
[System.Security.SecuritySafeCritical]
public String ToString(IFormatProvider provider) {
Contract.Ensures(Contract.Result<String>() != null);
return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider));
}
И вот тут я нашел то, что лично для меня показалось крайне удачным решением и, откровенно говоря, неявным ускорением о котором вообще не было мыслей:
public static NumberFormatInfo GetInstance(IFormatProvider formatProvider) {
// Fast case for a regular CultureInfo
NumberFormatInfo info;
CultureInfo cultureProvider = formatProvider as CultureInfo;
if (cultureProvider != null && !cultureProvider.m_isInherited) {
info = cultureProvider.numInfo;
if (info != null) {
return info;
}
else {
return cultureProvider.NumberFormat;
}
}
// Fast case for an NFI;
info = formatProvider as NumberFormatInfo;
if (info != null) {
return info;
}
if (formatProvider != null) {
info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;
if (info != null) {
return info;
}
}
return CurrentInfo;
}
А CultureInfo то проверяется первым, значит, есть вероятность выиграть слегка времени еще и на этом моменте, потому были сделаны те методы, которые были сделаны для тестов.
Стоит обратить внимание
// Сводка:
// Возвращает или задает объект System.Globalization.CultureInfo, представляющий
// язык и региональные параметры, используемые текущим потоком.
//
// Возврат:
// Объект, представляющий язык и региональные параметры, используемые текущим потоком.
//
// Исключения:
// T:System.ArgumentNullException:
// Для свойства задано значение null.
public static CultureInfo CurrentCulture { get; set; }
Для себя я сделал вывод, что выгодней всего делать следующим образом:
Результат
const int i = 424242;
CultureInfo culture = CultureInfo.CurrentCulture;
string str = i.ToString(culture);
Console.WriteLine(str);
Материал не претендует на научность, но позитив от чтения приветствуется.