Спрятал чтобы не сломать случайно интригу тем кто читает в плоском режиме.
Скрытый текст
У HeavyStructWrapper2 надо убрать у структуры readonly. Причина тормозов в том что компилятор C# не знает какие методы у структруры мутабельные а какие нет (имхо мог бы вычислять и аттрибутом помечать, либо могли добавить модификатор const/readonly как сделано у C++.
Без этого знания считается что все методы мутабельные, и CLR приходится для сохранения структуры в исходном состоянии вызывать .Test() только у копии. Т.е. перед каждым вызовом .Test() делается копия структуры, и это копирование загоняет всё в тормоза.
P.S. Почему-то у меня ускорение не в 10 а почти в 50 раз. Имхо такой разницы быть не должно, разбираюсь
Nemerle — power of metaprogramming, functional, object-oriented and imperative features in a statically-typed .NET language
S> public struct HeavyStructWrapper
S> {
S> private readonly HeavyStruct _h;
S> public decimal CallTest() => _h.Test();
S> }
S>
S>Чего не так-то?
Да, отличный пример! Я именно по этой причине в свой чудо анализатор ErrorProne.NET добавил правило UseReadOnlyAttributeAnalyzer, которое предлагает заменить ключевое слово readonly кастомным атрибутом:
public struct HeavyStructWrapper
{
[ReadOnly]
private HeavyStruct _h;
}
Теперь анализатор не позволит изменить _h за пределами конструктора, но при этом доступ к полю не будет приводить к копии.
Я вот подумал, что если бы анализатор детектил проблему по размеру используемой структуры (еще бы знать этот порог?) и вообще было бы хорошо!
Здравствуйте, SergeyT., Вы писали:
ST>Теперь анализатор не позволит изменить _h за пределами конструктора, но при этом доступ к полю не будет приводить к копии.
Угу. Как пожелание: в UseReadOnlyAttributeAnalyzer добавить учёт размера структуры. Из эмпирики: для < 8 байт можно вообще не показывать, для > 24 — варнинг надо.
Если нужны пруфы, то лучше меня не слушать, а взять бенчмарк (в ветке выше простенький был), запустить и посмотреть, начиная с какого размера падение совсем неприличное получается.
Кстати, а нет желания упросить товарищей из JetBrains включить аналогичные аннотации?
Они в профильном форуме отмечаются регулярно, если заведёшь тему — могу поддержать
UPD. Бгг. Вот это
ST>Я вот подумал, что если бы анализатор детектил проблему по размеру используемой структуры (еще бы знать этот порог?) и вообще было бы хорошо!
ST>>Теперь анализатор не позволит изменить _h за пределами конструктора, но при этом доступ к полю не будет приводить к копии.
S>Угу. Как пожелание: в UseReadOnlyAttributeAnalyzer добавить учёт размера структуры. Из эмпирики: для < 8 байт можно вообще не показывать, для > 24 — варнинг надо. S>Если нужны пруфы, то лучше меня не слушать, а взять бенчмарк (в ветке выше простенький был), запустить и посмотреть, начиная с какого размера падение совсем неприличное получается.
Да, у меня были похожие мысли. Только, кажется, нужно еще привязываться к архитектуре, поскольку пороги для x86 и x64 — разные.
S>Кстати, а нет желания упросить товарищей из JetBrains включить аналогичные аннотации?
Я чутка с этим делом еще поиграюсь, и можно будет их попросить это сделать!
S>Они в профильном форуме отмечаются регулярно, если заведёшь тему — могу поддержать
Здравствуйте, SergeyT., Вы писали: ST>Здравствуйте, Sinix, Вы писали:
ST>>>Теперь анализатор не позволит изменить _h за пределами конструктора, но при этом доступ к полю не будет приводить к копии. S>>Угу. Как пожелание: в UseReadOnlyAttributeAnalyzer добавить учёт размера структуры. Из эмпирики: для < 8 байт можно вообще не показывать, для > 24 — варнинг надо. S>>Если нужны пруфы, то лучше меня не слушать, а взять бенчмарк (в ветке выше простенький был), запустить и посмотреть, начиная с какого размера падение совсем неприличное получается. ST>Да, у меня были похожие мысли. Только, кажется, нужно еще привязываться к архитектуре, поскольку пороги для x86 и x64 — разные.
Мне кажется, тут надо исходить из того, влезает ли структура в cache line size, которая обычно 64 байта. Хотя и меньшая по размеру структура может 2 линии занимать.
А влияние x86 и x64 (кроме размера указателей) будет еще и в том, что JIT разный код генерит для копирования. Например, может rep movsd использовать, а может и xmm регистры.
Например, x86:
Скрытый текст
var hsw = new HeavyStructWrapper();
00AD34AA in al,dx
00AD34AB push edi
00AD34AC push esi
00AD34AD push ebx
00AD34AE sub esp,100h
00AD34B4 mov esi,ecx
00AD34B6 lea edi,[ebp-10Ch]
00AD34BC mov ecx,40h
00AD34C1 xor eax,eax
00AD34C3 rep stos dword ptr es:[edi]
00AD34C5 mov ecx,esi
00AD34C7 lea edi,[ebp-8Ch]
00AD34CD xor eax,eax
00AD34CF lea ecx,[eax+20h]
00AD34D2 rep stos dword ptr es:[edi]
Console.WriteLine(hsw.CallTest());
00AD34D4 lea edi,[ebp-10Ch]
00AD34DA lea ecx,[eax+20h]
00AD34DD rep stos dword ptr es:[edi]
00AD34DF lea edi,[ebp-10Ch]
00AD34E5 lea esi,[ebp-8Ch]
00AD34EB lea ecx,[eax+20h]
00AD34EE rep movs dword ptr es:[edi],dword ptr [esi]
00AD34F0 lea edx,[ebp-10Ch]
00AD34F6 mov ebx,dword ptr [edx]
00AD34F8 mov edi,dword ptr [edx+4]
00AD34FB mov esi,dword ptr [edx+8]
00AD34FE mov eax,dword ptr [edx+0Ch]
00AD3501 push eax
00AD3502 push esi
00AD3503 push edi
00AD3504 push ebx
00AD3505 call 71F10A74
00AD350A lea esp,[ebp-0Ch]
00AD350D pop ebx
00AD350E pop esi
00AD350F pop edi
00AD3510 pop ebp
00AD3511 ret