Comments 28
Охренеть, круто
+13
Мега статья!
Ее бы в мирных целях, в смысле на MSDN куда-нибудь пристроить, и орден на грудь, в смысле MVP получить!
Ее бы в мирных целях, в смысле на MSDN куда-нибудь пристроить, и орден на грудь, в смысле MVP получить!
+11
ну если без регисторов то остальное я в clr via C# уже вроде видел
0
Когда может понадобиться ручная работа с памятью, спросите Вы?
Например, для копирования массива байтов.
Зачем приведен такой тривиальный пример, если можно использовать готовые функции, такие как CopyTo() или Array.Copy() — они скопируют эффективнее. Да тут и вообще можно обойтись без unsafe.
В качестве примера (на fixed и unsafe) можно было бы написать что-нибудь с указателем на struct тип, потому что без неуправляемого кода не получится изменять его поля (свойства) без перекопирования.
Ну и что касается просто применений неуправляемого кода, то оно может потребоваться например для быстрого преобразования float в int или других типов:
float f = ...;
unsafe { int i = *(int*)&f; }
0
Регистры процессора || FastCall
Хорошо конечно что вы это расписали, но такой оптимизации уже сто лет в обед (помню еще в delphi), и не удивительно, что она реализована в JIT :)
Кстати, анализировать ассемблерный код через студию не слишком правильно, поскольку без дебага код сильнее оптимизирован (в частности, заинлайнены все свойства и небольшие методы).
В качестве новшеств .NET 4.5, касающихся методов, можно упомянуть атрибут MethodImplOptions.AggressiveInlining, который предписывает компилятору по возможности инлайнить метод.
+1
Ах, да, можно было бы еще вставить картинку для привлечения внимания)
+3
Здорово и полезно для начинающих разработчиков, добавил в избранное — дам коллегам почитать, всё вместе по этой теме ещё не находил.
Однако есть серьёзное замечание. То, что вы привели в качестве примера CER (Constrained Execution Region), никуда не годится и не будет работать: под CER попадает код не в блоке try, а в блоке finally. Хотите что-то сделать под CER — пишите:
У вас там есть finally, да, но он для другого, что ещё более запутывает
Ещё одно дополнение: в новом GC, что в 4.5, теперь появилось более оптимальная работа с LOH (как вы помните, там не проходит компактификация кучи, в отличие от GenX) — эксперименты команды разработчиков показывают, что LOH теперь фрагментируется гораздо меньше, а значит, шансы выделить память и уложиться в её меньший объём стали выше
Однако есть серьёзное замечание. То, что вы привели в качестве примера CER (Constrained Execution Region), никуда не годится и не будет работать: под CER попадает код не в блоке try, а в блоке finally. Хотите что-то сделать под CER — пишите:
System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
try {} finally { /*Важный код, который нельзя прерывать*/ }
У вас там есть finally, да, но он для другого, что ещё более запутывает
Ещё одно дополнение: в новом GC, что в 4.5, теперь появилось более оптимальная работа с LOH (как вы помните, там не проходит компактификация кучи, в отличие от GenX) — эксперименты команды разработчиков показывают, что LOH теперь фрагментируется гораздо меньше, а значит, шансы выделить память и уложиться в её меньший объём стали выше
0
Круто, отличная статья. Актуально ли данное решение в Java?
+2
Спасибо за статью. Она многое поставила на свои места, а то так то все известно(по частям) но как то общей картины не складывалось.
+1
Статья замечательная! Только вот открыл ее из Лучшее за 24 часа — думал что тут что-то про луну)). Отличный ход.
+1
Спасибо за статью.
+1
Кстати, почему вы пишете:
| Вообще, это уже третий тип в .NET — Pointer Type. Представляет он собой DWORD-адрес на конкретный экземпляр либого Value Type + bool.
Bool — это же тоже Value Type.
| Вообще, это уже третий тип в .NET — Pointer Type. Представляет он собой DWORD-адрес на конкретный экземпляр либого Value Type + bool.
Bool — это же тоже Value Type.
0
А я так надеялся увидеть
0
Понимаю, что пишу несколько поздно, но
Приведенный фрагмент кода доказывает обратное: m_firstChar — это и есть первый символ строки, поскольку у него берут адрес.
Ну и чтобы окончательно убедиться в том, что m_firstChar является указателем, рассмотрим, например, часть кода метода Join:
fixed (char* ptr = &text.m_firstChar) { UnSafeCharBuffer unSafeCharBuffer = new UnSafeCharBuffer(ptr, num); unSafeCharBuffer.AppendString(value[startIndex]); for (int j = startIndex + 1; j <= num2; j++) { unSafeCharBuffer.AppendString(separator); unSafeCharBuffer.AppendString(value[j]); } }
Приведенный фрагмент кода доказывает обратное: m_firstChar — это и есть первый символ строки, поскольку у него берут адрес.
0
т.е. Вы подразумеваете, что тип System.Char (он же char), представляющий собой символ юникода, конвертируется в указатель?
очень интересно, т.к. в данном случае знак 'A' превратится в адрес вида 0x000041 и по данному адресу будет передано управление? интересно…
очень интересно, т.к. в данном случае знак 'A' превратится в адрес вида 0x000041 и по данному адресу будет передано управление? интересно…
0
Нет. Лежит себе char 'A' по адресу, скажем, 0x12345678 — и именно это 0x12345678 и попадает в переменную ptr. А передача управления — это вообще из другой оперы.
Фактически, в строке
Фактически, в строке
fixed (char* ptr = &text.m_firstChar)
происходит обычная операция сложения — к адресу объекта text прибавляется смещение поля m_firstChar.0
вся проблема заключается в конструкции fixed (...), оно абсолютно ничего не делает в MSIL-коде. более того используются адреса объектов в стеке (если тип конвертируется в указатель). еще одним вопросом является то, что CLR автоматически обновляет адреса строк. обычно различие в m_firstChar будет наблюдаться в при активном создании и уничтожении строк. при объеме в 65 KB строк, GC активно начинает чистить память. не раз сталкивался с проблемой падения приложения, из-за такого рода вычисления значений.
еще одним моментом является то, что внутренняя структура строк и др. компонентов среды могут изменяться в зависимости от версии.
и еще попробуйте просто передать в неуправляемый код адрес m_firstChar для создания новых строк. так, например, JavaScriptCore может создавать строки путем копирования или без, если передать в конструктор js-строки адрес на массив символов.
запустите любой js-код для проверки значений и увидите результат.
еще одним моментом является то, что внутренняя структура строк и др. компонентов среды могут изменяться в зависимости от версии.
и еще попробуйте просто передать в неуправляемый код адрес m_firstChar для создания новых строк. так, например, JavaScriptCore может создавать строки путем копирования или без, если передать в конструктор js-строки адрес на массив символов.
запустите любой js-код для проверки значений и увидите результат.
0
Ну очевидно же, что вне fixed-блока сохраненным адресом пользоваться нельзя. Даже пытаться не буду.
Но все эти рассуждения нисколько не противоречат тому, что m_firstChar — не указатель.
Но все эти рассуждения нисколько не противоречат тому, что m_firstChar — не указатель.
0
мой предыдущий комментарий сводился к тому, чтобы передать адрес в неуправляемый код сразу:
а сохранять адрес fixed — по рукам надо.
дело в том, что в случае строк JIT-выбирает именно поле m_firstChar, минуя вычисления оффсета.
но уговаривать не буду ;)
fixed (char* ptr = somestring)
{
some_unmanged_code(ptr); //code copies string
}
а сохранять адрес fixed — по рукам надо.
дело в том, что в случае строк JIT-выбирает именно поле m_firstChar, минуя вычисления оффсета.
но уговаривать не буду ;)
0
Sign up to leave a comment.
Обратная сторона луны