В связи с пандемией, ситуация с безналичными платежами становится лучше, например, недавно, заплатил 90 центов в DM картой без проблем, был удивлен; в булочной, под боком, раньше нельзя было платить картой, теперь такая возможность появилась. Места где я не смог расплатиться картой в последнее время — пара забегаловок общепита, типа ларек; бургерная, но там я постоянный клиент, и часто есть всяки "плюшки" в виде бесплатной колы или картошки фри; цветочный магазин (картой можно оплатить от 10 евро).
Единого официального мобильного приложения для госулуг нет, но есть местные сайты. Недавно был полностью онлайн оформлен документ, разрешающий бесплатно парковаться около дома. Место действия Дюссельдорф.
Да, штраф за неправильную парковку, придется оплатить не из единого приложения, а самому, через онлайн банк, так как перевод нужно будет сделать на счет города, где произошло недоразумение и деньги сразу поступят в местный бюджет.
Ситуация с мобильным интернетом улучшается, например, наконец-то, появился появился действительно безлимитный мобильный интернет (30 евро в месяц).
Про сервис. На прошлой неделе был ТО моего автомобиля. Приехал в автосервис утром, оставил машину, мне было вызвано бесплатное такси, которые довезло меня до работы, а вечером снова бесплатно довезло меня от работы до сервиса. Это был автосервис местного ВАЗ.
Так что, для меня лично, "стакак на половину полон".
Мужу Марина решила ничего не говорить — расскажет тогда, когда получит свою первую зарплату.
Мужи! Будьте внимательнее к своим вторым половинам. Им так же тяжело, и они пытаются не быть мнимой "обузой" семье, даже такими неразумными способами. Старайтесь создать в семье доверительную атмосферу, когда любые важные решения сначала обсуждаются на семейном совете.
Так в каком же году родился Иисус? 01-01-01 по традиционному летоисчислению(напомню, мы не вдаёмся в подробности смещения календарей, это отдельная, крайне интересная тема). Поскольку мы считаем даты от рождества христова, он родился в в первый день первого месяца первого года.
Написал тест для измерения сравнивания строк через == .ToUpper() и .Equals
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
namespace CompareStrinngsTest
{
public class Benchmarks
{
[Params(10, 100, 1000)]
public int StringLength = 10;
public int ListSize = 1000;
private List<string> _lst;
private string _strToFind;
private string _strToFindUpper;
private static Random random = new Random();
private static string RandomString(int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
[GlobalSetup]
public void Setup() {
_strToFind = RandomString(StringLength);
_strToFindUpper = _strToFind.ToUpperInvariant();
_lst = new List<string>(ListSize);
for (int i = 0; i < ListSize; i++) {
if (i == ListSize / 2) {
_lst.Add(_strToFind);
}
else {
_lst.Add(RandomString(StringLength));
}
}
}
[Benchmark]
public int CountUpperBoth() {
return _lst.Count(item => item.ToUpperInvariant() == _strToFind.ToUpperInvariant());
}
[Benchmark]
public int CountUpperSingle() {
return _lst.Count(item => item.ToUpperInvariant() == _strToFindUpper);
}
[Benchmark]
public int CountEqualIgnoreCase() {
return _lst.Count(item => item.Equals(_strToFind, StringComparison.InvariantCultureIgnoreCase));
}
}
}
Результаты
Intel Core i5-8265U CPU 1.60GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.201
[Host] : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT
DefaultJob : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT
| Method | StringLength | Mean | Error | StdDev |
|--------------------- |------------- |----------:|----------:|----------:|
| CountUpperBoth | 10 | 61.34 us | 0.432 us | 0.383 us |
| CountUpperSingle | 10 | 41.28 us | 0.216 us | 0.202 us |
| CountEqualIgnoreCase | 10 | 53.66 us | 0.251 us | 0.196 us |
| CountUpperBoth | 100 | 159.48 us | 1.247 us | 1.166 us |
| CountUpperSingle | 100 | 91.42 us | 0.478 us | 0.447 us |
| CountEqualIgnoreCase | 100 | 53.80 us | 0.622 us | 0.486 us |
| CountUpperBoth | 1000 | 993.25 us | 14.811 us | 26.327 us |
| CountUpperSingle | 1000 | 518.15 us | 3.508 us | 3.281 us |
| CountEqualIgnoreCase | 1000 | 54.46 us | 0.265 us | 0.221 us |
Резюме:
Tест CountUpperBoth самый медленный. В этом тесте используется сравнение item.ToUpperInvariant() == _strToFind.ToUpperInvariant(), точно такое же что у вас в методе ContactHydratorBase.GetPropertiesValues.
Tест CountUpperSingle быстрее предыдущего. В этом тесте используется сравнение item.ToUpperInvariant() == _strToFindUpper, где _strToFindUpper референсная строка заранее преобразованная в верхний регистр. Это решение я предлагал в коментарии выше.
Tест CountEqualIgnoreCase самый быстрый тест для длинных строк. В этом тесте используется сравнение item.Equals(_strToFind, StringComparison.InvariantCultureIgnoreCase). Это решение Вы используете в методе ContactHydratorBase.GetPropertiesValuesWithoutLinq.
Вывод:
Ваше утверждение
По производительности в том числе они почти эквивалентны.
некорректно.
Для тестирования реальной скорости выполнения методов, с Linq и без, в Ваших тестах, надо использовать одинаковые функции/операторы сравнения строк в методах ContactHydratorBase.GetPropertiesValues и ContactHydratorBase.GetPropertiesValuesWithoutLinq.
В коде x.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant(), свойство _typeName преобразуется в верхний регистр в каждом вызове.
Это избыточная операция. Если я не прав, то какой смысл преобразовывать свойство _typeName в верхний регистр в каждом вызове метода GetPropertiesValues
Достаточно написать
static ContactHydratorBase()
{
var type = typeof(Contact);
_typeName = type.FullName.ToUpperInvariant();
}
//....
private async Task<PropertyToValueCorrelation[]> GetPropertiesValues(string rawData, CancellationToken abort)
{
var mailPairs = _normalizer.ParseWithLinq(rawData: rawData, pairDelimiter: Environment.NewLine);
var mapSchemas =
_mapSchemas
.Where(x => x.EntityName.ToUpperInvariant() == _typeName)
.Select(x => new { x.Key, x.Property })
.ToArray();
return
mailPairs
.Join(mapSchemas, x => x.Key, x => x.Key,
(x, y) => new PropertyToValueCorrelation { PropertyName = y.Property, Value = x.Value })
.ToArray();
}
и Linq-тесты начнут выполняться быстрее тестов без Linq.
По производительности в том числе они почти эквивалентны.
Я не уверен, надо бы написать тест.
Кажется я нашел проблему в Вашем коде, которая делает Linq-тесты медленнее. У Вас происходит преобразование своейства _typeName в верхний регистр в каждой итерации. Достаточно инициализировать это свойсво строкой в верхнем регистре, т.е. _typeName = type.FullName.ToUpperInvariant(); и ниже сравнивать как .Where(x => x.EntityName.ToUpperInvariant() == _typeName)
После такого изменения, Linq-тесты работают быстрее, для тестов с 10 и более итераций.
Вот результаты откоректированных тестов
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.815 (1909/November2018Update/19H2)
Intel Core i5-8265U CPU 1.60GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.201
[Host] : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT
DefaultJob : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT
| Method | N | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------- |----- |------------:|----------:|----------:|-----------:|-------:|------:|------------:|
| ManualHydrationLinq | 1 | 190.3 us | 0.79 us | 0.61 us | 20.0195 | - | - | 62.03 KB |
| ManualHydration | 1 | 197.0 us | 1.04 us | 0.97 us | 12.6953 | 4.3945 | - | 39.39 KB |
| FastHydration | 1 | 200.7 us | 1.09 us | 0.96 us | 13.9160 | 4.6387 | - | 42.9 KB |
| SlowHydrationLinq | 1 | 201.0 us | 1.34 us | 1.25 us | 21.4844 | - | - | 66.36 KB |
| SlowHydration | 1 | 204.4 us | 0.69 us | 0.61 us | 14.1602 | 4.8828 | - | 43.7 KB |
| ManualHydrationLinq | 10 | 451.5 us | 3.59 us | 3.36 us | 124.0234 | - | - | 379.8 KB |
| FastHydrationLinq | 10 | 490.6 us | 5.70 us | 5.33 us | 135.7422 | - | - | 414.97 KB |
| FastHydrationLinq | 1 | 507.2 us | 38.36 us | 101.05 us | - | - | - | 69.91 KB |
| SlowHydrationLinq | 10 | 526.7 us | 10.44 us | 11.17 us | 137.6953 | - | - | 423.09 KB |
| ManualHydration | 10 | 542.4 us | 3.05 us | 2.85 us | 49.8047 | - | - | 153.47 KB |
| FastHydration | 10 | 599.6 us | 8.18 us | 7.65 us | 61.5234 | - | - | 188.51 KB |
| SlowHydration | 10 | 607.4 us | 3.43 us | 3.04 us | 63.4766 | - | - | 196.57 KB |
| ManualHydrationLinq | 100 | 2,917.6 us | 30.89 us | 24.11 us | 1164.0625 | - | - | 3558.41 KB |
| FastHydrationLinq | 100 | 3,290.5 us | 11.59 us | 10.84 us | 1277.3438 | - | - | 3908.44 KB |
| SlowHydrationLinq | 100 | 3,590.5 us | 17.10 us | 16.00 us | 1304.6875 | - | - | 3989.66 KB |
| ManualHydration | 100 | 4,159.8 us | 73.01 us | 64.72 us | 421.8750 | - | - | 1294.29 KB |
| FastHydration | 100 | 4,308.2 us | 16.49 us | 15.42 us | 531.2500 | - | - | 1644.62 KB |
| SlowHydration | 100 | 4,621.8 us | 25.11 us | 23.49 us | 562.5000 | - | - | 1725.93 KB |
| ManualHydrationLinq | 1000 | 26,088.9 us | 145.04 us | 128.58 us | 11531.2500 | - | - | 35318.53 KB |
| FastHydrationLinq | 1000 | 30,015.9 us | 392.34 us | 347.80 us | 12656.2500 | - | - | 38818.53 KB |
| SlowHydrationLinq | 1000 | 33,952.4 us | 614.22 us | 574.54 us | 12933.3333 | - | - | 39631.04 KB |
| ManualHydration | 1000 | 38,119.2 us | 324.99 us | 304.00 us | 4076.9231 | - | - | 12693.55 KB |
| FastHydration | 1000 | 40,715.2 us | 322.91 us | 286.25 us | 5230.7692 | - | - | 16193.55 KB |
| SlowHydration | 1000 | 44,197.9 us | 366.73 us | 343.04 us | 5500.0000 | - | - | 17005.88 KB |
Опять же получается проблема с низкой скоростью выполнения Linq-тестов, не в Linq, а в бизнес-логике.
Хм. Посмотрел результаты Вашего ретеста и там Fast-тесты выполняются быстрее Slow-тестов при 10, 100 и 1000 итераций. Это без каких либо изменений моих изменений в вашем коде.
Т.е. в любом случае Ваш первоначальный вывод, что
Методы, победоносно носящие префикс Fast, почти при всех проходах оказываются медленнее, чем методы с префиксом Slow.
Методы, победоносно носящие префикс Fast, почти при всех проходах оказываются медленнее, чем методы с префиксом Slow.
LINQ сожрет производительность сильней.
IMHO, процитированные целое и часть предложения некорректны, в свете вышеуказанных результатов тестов с измененным кодом. И код был изменен, в первом случае, чтобы использовать словарь, как и было Вами задумано, а во втором случае, чтобы заставить тест показать разницу скорости работы кода с Linq и без него, а не сравнивать производительность x.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant() и x.EntityName.Equals(_typeName, StringComparison.InvariantCultureIgnoreCase).
Первое — сравнение строк в верхнем регистре — это по сути и есть эквалс, но!
По сути да, но не по производительности. В тегах указана "высокая производительность".
Вам следует на будущее учитывать, что EF не понимает Equals, и извлечение из бд нужно делать по оператору ==. Если будете ставить Equals, EF не сможет транслировать код в SQL.
IMHO, такие вещи должны решаться, во время добавления данных в БД, т.е. данные для сравнения без учета регистра, должны быть сохранены в БД или в верхнем или нижнем регистрах, если опять же речь про высокую производительность.
Если говорить про БД, то есть высокая вероятность, что выборка всей таблицы ContactMapSchemas в методе GetPropertiesValuesWithoutLinq сведет к минимуму преимущества в производительности.
Ещё одно узкое место в реализации автором метода ContactHydratorBase.GetPropertiesValues — использование x.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant() вместо x.EntityName.Equals(_typeName, StringComparison.InvariantCultureIgnoreCase). Так же был удален ну нужный вызов .TaArray() в конце.
Я правильно понимаю, что замена метода FastContactHydrator.GetContact на метод похожий на нижеследующий, выполняет те же функции, что и код автора и должен улучшить время тестов для этого класса?
protected override Contact GetContact(PropertyToValueCorrelation[] correlations)
{
var contact = new Contact();
foreach (var corrItem in correlations) {
if (_proprtySettersMap.TryGetValue(corrItem.PropertyName, out var action)) {
action(contact, corrItem.Value);
}
}
return contact;
}
Какой смысл в использовании Dictionary в классе FastContactHydrator? На сколько я понимаю, все его использование сводится к инициализации и циклом по всем его элементам. Поиска в словаре я не нашел.
Подводные камни есть. По слухам с клиентами разрывали контракты, когда они скачивали, кто больше 500 гб, кто больше 1 тб в месяц, но это не мой вариант, у меня за декабрь всего 70 гб на сегодняшний день, был день когда было скачено 10 гб. Пока без проблем, никакого дросселирования.
Пара мыслей про Германию.
В связи с пандемией, ситуация с безналичными платежами становится лучше, например, недавно, заплатил 90 центов в DM картой без проблем, был удивлен; в булочной, под боком, раньше нельзя было платить картой, теперь такая возможность появилась. Места где я не смог расплатиться картой в последнее время — пара забегаловок общепита, типа ларек; бургерная, но там я постоянный клиент, и часто есть всяки "плюшки" в виде бесплатной колы или картошки фри; цветочный магазин (картой можно оплатить от 10 евро).
Единого официального мобильного приложения для госулуг нет, но есть местные сайты. Недавно был полностью онлайн оформлен документ, разрешающий бесплатно парковаться около дома. Место действия Дюссельдорф.
Да, штраф за неправильную парковку, придется оплатить не из единого приложения, а самому, через онлайн банк, так как перевод нужно будет сделать на счет города, где произошло недоразумение и деньги сразу поступят в местный бюджет.
Ситуация с мобильным интернетом улучшается, например, наконец-то, появился появился действительно безлимитный мобильный интернет (30 евро в месяц).
Про сервис. На прошлой неделе был ТО моего автомобиля. Приехал в автосервис утром, оставил машину, мне было вызвано бесплатное такси, которые довезло меня до работы, а вечером снова бесплатно довезло меня от работы до сервиса. Это был автосервис местного ВАЗ.
Так что, для меня лично, "стакак на половину полон".
Мужи! Будьте внимательнее к своим вторым половинам. Им так же тяжело, и они пытаются не быть мнимой "обузой" семье, даже такими неразумными способами. Старайтесь создать в семье доверительную атмосферу, когда любые важные решения сначала обсуждаются на семейном совете.
А как же Mac Pro?
Новый год начинается 1-го января. Эта дата не связана с чьим-то днем рождения. Согласно Википедии 1-ое января, как первый день нового года установлем Юлием Цезарем в 46-м году до нашей эры.
Новая эра привязана к году, а не к дате рождения Иисуса.
Опять же, согласно Википедии он родился в 1-ом году нашей эры.
ИМХО, день рождения Иисуса, без учета смещения календарей, — 25.12.01. Поправте меня, если я ошибаюсь.
Википедия сообщает, что Иисус родился 25 декабря (7 января) или 6 января.
Резюме:
CountUpperBoth
самый медленный. В этом тесте используется сравнениеitem.ToUpperInvariant() == _strToFind.ToUpperInvariant()
, точно такое же что у вас в методеContactHydratorBase.GetPropertiesValues
.CountUpperSingle
быстрее предыдущего. В этом тесте используется сравнениеitem.ToUpperInvariant() == _strToFindUpper
, где_strToFindUpper
референсная строка заранее преобразованная в верхний регистр. Это решение я предлагал в коментарии выше.CountEqualIgnoreCase
самый быстрый тест для длинных строк. В этом тесте используется сравнениеitem.Equals(_strToFind, StringComparison.InvariantCultureIgnoreCase)
. Это решение Вы используете в методеContactHydratorBase.GetPropertiesValuesWithoutLinq
.Вывод:
Ваше утверждение
некорректно.
Для тестирования реальной скорости выполнения методов, с Linq и без, в Ваших тестах, надо использовать одинаковые функции/операторы сравнения строк в методах
ContactHydratorBase.GetPropertiesValues
иContactHydratorBase.GetPropertiesValuesWithoutLinq
.Попробую объяснить по другому.
В коде
x.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant()
, свойство_typeName
преобразуется в верхний регистр в каждом вызове.Это избыточная операция. Если я не прав, то какой смысл преобразовывать свойство
_typeName
в верхний регистр в каждом вызове методаGetPropertiesValues
Достаточно написать
и Linq-тесты начнут выполняться быстрее тестов без Linq.
Я не уверен, надо бы написать тест.
Кажется я нашел проблему в Вашем коде, которая делает Linq-тесты медленнее. У Вас происходит преобразование своейства
_typeName
в верхний регистр в каждой итерации. Достаточно инициализировать это свойсво строкой в верхнем регистре, т.е._typeName = type.FullName.ToUpperInvariant();
и ниже сравнивать как.Where(x => x.EntityName.ToUpperInvariant() == _typeName)
После такого изменения, Linq-тесты работают быстрее, для тестов с 10 и более итераций.
Опять же получается проблема с низкой скоростью выполнения Linq-тестов, не в Linq, а в бизнес-логике.
Хм. Посмотрел результаты Вашего ретеста и там Fast-тесты выполняются быстрее Slow-тестов при 10, 100 и 1000 итераций. Это без каких либо изменений моих изменений в вашем коде.
Т.е. в любом случае Ваш первоначальный вывод, что
не корректен.
В Вашей статье написано
IMHO, процитированные целое и часть предложения некорректны, в свете вышеуказанных результатов тестов с измененным кодом. И код был изменен, в первом случае, чтобы использовать словарь, как и было Вами задумано, а во втором случае, чтобы заставить тест показать разницу скорости работы кода с Linq и без него, а не сравнивать производительность
x.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant()
иx.EntityName.Equals(_typeName, StringComparison.InvariantCultureIgnoreCase)
.По сути да, но не по производительности. В тегах указана "высокая производительность".
IMHO, такие вещи должны решаться, во время добавления данных в БД, т.е. данные для сравнения без учета регистра, должны быть сохранены в БД или в верхнем или нижнем регистрах, если опять же речь про высокую производительность.
Если говорить про БД, то есть высокая вероятность, что выборка всей таблицы
ContactMapSchemas
в методеGetPropertiesValuesWithoutLinq
сведет к минимуму преимущества в производительности.Ещё одно узкое место в реализации автором метода
ContactHydratorBase.GetPropertiesValues
— использованиеx.EntityName.ToUpperInvariant() == _typeName.ToUpperInvariant()
вместоx.EntityName.Equals(_typeName, StringComparison.InvariantCultureIgnoreCase)
. Так же был удален ну нужный вызов.TaArray()
в конце.Результат ниже. Как видно Linq не так уж и плох. При 100 итерациях — самый быстрый тест.
Обновление: В тесте
FastHydrationLinq2
используется модифицированный классFastContactHydrator2
, код которого можно найти выше в моём коментарии.Добавил в код автора откорректироанный класс FastContactHydrator, с использованием словаря как задумывалось.
Получились следующие результаты. Намерянно убрал тесты для 1000 итераций, ибо долго ждать.
Я правильно понимаю, что замена метода
FastContactHydrator.GetContact
на метод похожий на нижеследующий, выполняет те же функции, что и код автора и должен улучшить время тестов для этого класса?Какой смысл в использовании
Dictionary
в классеFastContactHydrator
? На сколько я понимаю, все его использование сводится к инициализации и циклом по всем его элементам. Поиска в словаре я не нашел.