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

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

Охренеть, круто
Мега статья!
Ее бы в мирных целях, в смысле на MSDN куда-нибудь пристроить, и орден на грудь, в смысле MVP получить!
ну если без регисторов то остальное я в clr via C# уже вроде видел
Clr via C#, 2 издание, глава 20 (Сборка мусора), страница 451, 3-ий абзац, там про регистры таки написано.
ну значит тогда совсем все было в книге а про регистры я уже забыл.

P.S. 2 это которое уже вроде про .net 4 было?
Нет, это зелененькая книга по clr 2.
Джеффри Рихтер — CLR via C#. Программирование на платформе Microsoft .NET Framework 2.0 на языке C#
Когда может понадобиться ручная работа с памятью, спросите Вы?

Например, для копирования массива байтов.


Зачем приведен такой тривиальный пример, если можно использовать готовые функции, такие как CopyTo() или Array.Copy() — они скопируют эффективнее. Да тут и вообще можно обойтись без unsafe.

В качестве примера (на fixed и unsafe) можно было бы написать что-нибудь с указателем на struct тип, потому что без неуправляемого кода не получится изменять его поля (свойства) без перекопирования.

Ну и что касается просто применений неуправляемого кода, то оно может потребоваться например для быстрого преобразования float в int или других типов:

float f = ...;   
unsafe { int i = *(int*)&f; }


>Зачем приведен такой тривиальный пример, если можно использовать готовые функции, такие как CopyTo() или Array.Copy()

согласен, что пример несколько тривиален, но мы же в статье изучаем возможности применения, а использование BCL, не так ли?
Регистры процессора || FastCall


Хорошо конечно что вы это расписали, но такой оптимизации уже сто лет в обед (помню еще в delphi), и не удивительно, что она реализована в JIT :)
Кстати, анализировать ассемблерный код через студию не слишком правильно, поскольку без дебага код сильнее оптимизирован (в частности, заинлайнены все свойства и небольшие методы).

В качестве новшеств .NET 4.5, касающихся методов, можно упомянуть атрибут MethodImplOptions.AggressiveInlining, который предписывает компилятору по возможности инлайнить метод.
Ах, да, можно было бы еще вставить картинку для привлечения внимания)

Здорово и полезно для начинающих разработчиков, добавил в избранное — дам коллегам почитать, всё вместе по этой теме ещё не находил.

Однако есть серьёзное замечание. То, что вы привели в качестве примера CER (Constrained Execution Region), никуда не годится и не будет работать: под CER попадает код не в блоке try, а в блоке finally. Хотите что-то сделать под CER — пишите:

System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
try {} finally { /*Важный код, который нельзя прерывать*/ }

У вас там есть finally, да, но он для другого, что ещё более запутывает

Ещё одно дополнение: в новом GC, что в 4.5, теперь появилось более оптимальная работа с LOH (как вы помните, там не проходит компактификация кучи, в отличие от GenX) — эксперименты команды разработчиков показывают, что LOH теперь фрагментируется гораздо меньше, а значит, шансы выделить память и уложиться в её меньший объём стали выше
>под CER попадает код не в блоке try, а в блоке finally

по-моему, это всегда оговаривается, если изучать CER :)

здесь главное — восстановить состояние GC в oldMode.
GCSettings.LatencyMode = oldMode;
Круто, отличная статья. Актуально ли данное решение в Java?
что именно подразумевается под «данным решением»?)
Спасибо за статью. Она многое поставила на свои места, а то так то все известно(по частям) но как то общей картины не складывалось.
Статья замечательная! Только вот открыл ее из Лучшее за 24 часа — думал что тут что-то про луну)). Отличный ход.
Спасибо за статью.
Кстати, почему вы пишете:
| Вообще, это уже третий тип в .NET — Pointer Type. Представляет он собой DWORD-адрес на конкретный экземпляр либого Value Type + bool.

Bool — это же тоже Value Type.
все правильно! это издержки CTRL+C :)
фраза должна была быть немного другой, но остался bool.
А я так надеялся увидеть
Понимаю, что пишу несколько поздно, но
Ну и чтобы окончательно убедиться в том, что 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 — это и есть первый символ строки, поскольку у него берут адрес.
т.е. Вы подразумеваете, что тип System.Char (он же char), представляющий собой символ юникода, конвертируется в указатель?

очень интересно, т.к. в данном случае знак 'A' превратится в адрес вида 0x000041 и по данному адресу будет передано управление? интересно…
Нет. Лежит себе char 'A' по адресу, скажем, 0x12345678 — и именно это 0x12345678 и попадает в переменную ptr. А передача управления — это вообще из другой оперы.

Фактически, в строке fixed (char* ptr = &text.m_firstChar) происходит обычная операция сложения — к адресу объекта text прибавляется смещение поля m_firstChar.
вся проблема заключается в конструкции fixed (...), оно абсолютно ничего не делает в MSIL-коде. более того используются адреса объектов в стеке (если тип конвертируется в указатель). еще одним вопросом является то, что CLR автоматически обновляет адреса строк. обычно различие в m_firstChar будет наблюдаться в при активном создании и уничтожении строк. при объеме в 65 KB строк, GC активно начинает чистить память. не раз сталкивался с проблемой падения приложения, из-за такого рода вычисления значений.

еще одним моментом является то, что внутренняя структура строк и др. компонентов среды могут изменяться в зависимости от версии.

и еще попробуйте просто передать в неуправляемый код адрес m_firstChar для создания новых строк. так, например, JavaScriptCore может создавать строки путем копирования или без, если передать в конструктор js-строки адрес на массив символов.

запустите любой js-код для проверки значений и увидите результат.
Ну очевидно же, что вне fixed-блока сохраненным адресом пользоваться нельзя. Даже пытаться не буду.

Но все эти рассуждения нисколько не противоречат тому, что m_firstChar — не указатель.
мой предыдущий комментарий сводился к тому, чтобы передать адрес в неуправляемый код сразу:
fixed (char* ptr = somestring)
{
    some_unmanged_code(ptr); //code copies string
}


а сохранять адрес fixed — по рукам надо.

дело в том, что в случае строк JIT-выбирает именно поле m_firstChar, минуя вычисления оффсета.

но уговаривать не буду ;)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории