Unsafe cast’инг в .Net

Решил я тут на днях позаниматься немного программизмом, причем не для работы, а так, для себя, точнее для своего телефона ;). Стояла такая задача — загрузить из бинарного файла массив float’ов, причем сделать это максимально эффективно в плане быстродействия (под телефон же все-таки).


Оказалось, что в дотнете сделать это не так-то просто. Есть 2 пути — первое — это использовать BinaryReader и в цикле считывать из файла числа, и второй — прочитать весь файл в массив байт (byte[]) и потом из него копировать во float[]. И оба этих варианта меня понятное дело не устроили — первый потому что это медленно (насколько именно я не замерял конечно, но даже на вызовы функций уйдет время довольно приличное, если большое количество чисел надо прочитать), а второй — потому что это займет двойной объем памяти — сначала под массив байт, потом под массив float’ов.

Идеальным вариантом было бы конечно прочитать сразу все данные файла в массив байт и потом просто привести его к float[], тогда и прочитаем быстро и памяти лишней не сожрем. Но в дотнете этого сделать нельзя!

Очень расстроенный я полез искать решение в Интернетах, и обнаружил что я не один такой -)))) На многих форумах задавался тот же вопрос и на него не было ответа, и лишь когда я уже совсем потерял надежду и начал рвать на жопе волосы подумывать переписать все на сиплюплюсе, то наткнулся на гениальное решение одного омерекоса. Он предложил нае.. обмануть дотнет следующим образом: использовать класс, который использует явный StructLayout для своих членов, иными словами:

То есть класс содержит массив байт и массив float’ов, но они находятся в одном и том же месте. То есть мы считываем массив байт из файла, а потом можем просто обращаться к нему как массиву float. Урра, товарищи! Теперь как использовать это в коде:

Но здесь есть несколько моментов, о которых нельзя забывать. Так как массивы используют одну и ту же область памяти, то

1) в режиме отладки просмотр этих значений невозможен и Watch для них некорректно работает (студия слегка сходит с ума), то есть если поставить брейкпойнт и навести мышь на buffer.floats, то он покажет что это массив байт -)))). Но во время выполнения программа работает корректно.

2) неправильно определяется длина массива. Если например сделать buffer.bytes = new byte[4000], то buffer.floats.Length тоже будет 4000, хотя на самом деле их всего 1000, об этом нужно помнить и не пытаться читать писать за пределами массива. Насколько я понял — длина массива показывается в элементах того массива, который был первый создан

3) (это только догадка, проверять мне было лень:) Если проинициализировать оба массива, то возможно будет утечка памяти -))))

Ну и естественно эти преобразования работают только для value-типов по вполне понятным причинам.

Вот и все, теперь вы можете делать любые небезопасные касты для value-типов, удачного вам разрушения heap’a! 😉


Похожие записи:

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля отмечены *