Вопрос

В настоящее время я работаю над трассировщиком лучей на C # в качестве хобби-проекта.Я пытаюсь достичь приличной скорости рендеринга, реализуя некоторые трюки из реализации на c ++, и столкнулся с проблемой.

Объекты в сценах, которые визуализирует трассировщик лучей, хранятся в структуре KDTree, а узлы дерева, в свою очередь, хранятся в массиве.Оптимизация, с которой у меня возникают проблемы, заключается в попытке поместить как можно больше узлов дерева в строку кэша.Одним из способов сделать это является то, чтобы узлы содержали указатель только на левый дочерний узел.Тогда подразумевается, что правый дочерний элемент следует непосредственно за левым в массиве.

Узлы являются структурами, и при построении дерева они успешно помещаются в массив классом static memory manager.Когда я начинаю обходить дерево, поначалу кажется, что оно работает просто отлично.Затем в какой-то момент на ранней стадии рендеринга (каждый раз примерно в одном и том же месте) левый дочерний указатель корневого узла внезапно указывает на нулевой указатель.Я пришел к выводу, что сборщик мусора переместил структуры, поскольку массив лежит в куче.

Я пробовал несколько способов закрепления адресов в памяти, но ни один из них, похоже, не сохраняется в течение всего срока службы приложения, как мне нужно.Ключевое слово 'fixed', по-видимому, помогает только при вызовах отдельных методов, а объявление "фиксированных" массивов может быть выполнено только для простых типов, к которым не относится узел.Есть ли хороший способ сделать это, или я просто слишком далеко продвинулся по пути вещей, для которых C # не предназначался.

Кстати, переход на c ++, хотя, возможно, это лучший выбор для высокопроизводительной программы, не является вариантом.

Это было полезно?

Решение

Во-первых, если вы используете C # нормально, вы не можете внезапно получить нулевую ссылку из-за движущихся объектов сборщика мусора, потому что сборщик мусора также обновляет все ссылки, поэтому вам не нужно беспокоиться о его перемещении. вещи вокруг.

Вы можете закрепить вещи в памяти, но это может вызвать больше проблем, чем решить. Во-первых, он не позволяет сборщику мусора должным образом сжимать память и может повлиять на производительность таким образом.

Одна вещь, которую я бы сказал из вашего поста, это то, что использование структур может не помочь производительности, как вы надеетесь. C # не может встроить любые вызовы методов, включающие структуры, и хотя они исправили это в своей последней бета-версии времени выполнения, структуры часто не работают так хорошо.

Лично я бы сказал, что подобные трюки C ++ обычно не слишком хорошо переносятся в C #. Возможно, вам придется научиться отпускать немного; могут быть и другие, более тонкие способы повышения производительности;)

Другие советы

Что на самом деле делает ваш менеджер статической памяти?Если он не выполняет что-то небезопасное (P / Invoke, небезопасный код), поведение, которое вы видите, является ошибкой в вашей программе, а не из-за поведения среды CLR.

Во-вторых, что вы подразумеваете под "указателем" в отношении связей между структурами?Вы буквально имеете в виду небезопасный указатель KDTree *?Не делай этого.Вместо этого используйте индекс в массиве.Поскольку я ожидаю, что все узлы для одного дерева хранятся в одном массиве, вам не понадобится отдельная ссылка на массив.Достаточно будет одного индекса.

Наконец, если вам действительно необходимо использовать указатели KDTree *, то ваш менеджер статической памяти должен выделить большой блок, используя, напримерМаршал.AllocHGlobal или другой неуправляемый источник памяти;оба они должны обрабатывать этот большой блок как массив KDTree (т. е.индексировать в стиле KDTree* C) и он должен выделить узлы из этого массива, наткнувшись на указатель "free".

Если вам когда-нибудь придется изменить размер этого массива, то вам, конечно, нужно будет обновить все указатели.

Основной урок здесь заключается в том, что небезопасные указатели и управляемая память делают нет микширование за пределами "фиксированных" блоков, которые, конечно, имеют сходство с кадром стека (т.е.когда функция возвращается, закрепленное поведение исчезает).Существует способ закрепления произвольных объектов, таких как ваш массив, с помощью GCHandle.Alloc(YourArray, GCHandleType.Закреплено), но вы почти наверняка не захотите идти по этому пути.

Вы получите более разумные ответы, если более подробно опишете, что вы делаете.

Если вы действительно хотите это сделать, вы можете использовать метод GCHandle.Alloc, чтобы указать, что указатель должен быть закреплен без автоматического освобождения в конце области действия, как оператор fixed.

Но, как говорили другие люди, это оказывает чрезмерное давление на сборщик мусора. Как насчет простого создания структуры, которая содержит пару ваших узлов, и затем управления массивом NodePairs, а не массивом узлов?

Если вы действительно хотите иметь полностью неуправляемый доступ к куску памяти, вам, вероятно, лучше распределить память непосредственно из неуправляемой кучи, а не фиксировать часть управляемой кучи на постоянной основе (это препятствует работе кучи). в состоянии правильно компактировать себя). Одним из быстрых и простых способов сделать это было бы использование метода Marshal.AllocHGlobal.

Действительно ли запрещено хранить пару ссылок на массив и индекс?

Что на самом деле делает ваш менеджер статической памяти?Если он не выполняет что-то небезопасное (P / Invoke, небезопасный код), поведение, которое вы видите, является ошибкой в вашей программе, а не из-за поведения среды CLR.

На самом деле я говорил о небезопасных указателях.То, что я хотел, было чем-то вроде Marshal.AllocHGlobal, хотя и со сроком службы, превышающим один вызов метода.Поразмыслив, кажется, что просто использование индекса является правильным решением, поскольку я, возможно, слишком увлекся имитацией кода на c ++.

Одна вещь, которую я бы сказал из вашего поста, заключается в том, что использование структур может не повысить производительность, как вы надеетесь.C # не удается встроить какие-либо вызовы методов, связанные со структурами, и, хотя они исправили это в своей последней бета-версии во время выполнения, структуры часто работают не так хорошо.

Я немного изучил это и вижу, что это было исправлено в .NET 3.5SP1;Я предполагаю, что это то, что вы имели в виду под бета-версией во время выполнения.На самом деле, теперь я понимаю, что это изменение привело к удвоению скорости моего рендеринга.Теперь структуры активно интегрируются, что значительно повышает их производительность в системах X86 (в X64 производительность struct была выше заранее).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top