Обёртка над массивом 2^26 байт. Оптимизация
C#4+, XNA4.0
Есть структура Color - обёртка над Int32.
Есть класс Bitmap - обёртка над Color[], описывает изображение.
Класс содержит методы для прямого манипулирования элементами массива Color[], для изменения этого изображения. Рисование линий, отражение, изменение размеров, заливка, etc.
Каждый элемент массива Color[] описывает один пиксель. Индекс в массиве вычисляется так: index = y * width + x;, где width - ширина изображения.
Никаких проблем нет, но есть вопросы - нужно ли как-нибудь оптимизировать работу данного класса?
Например, что эффективнее - Color[], Color[,] или Color[][] ? Или вообще стоит хранить их не цветами, а int'ами?
При работе с текстурами 4096х4096, получаем в памяти порядка 64 мб. Нужно ли как-нибудь освобождать такой массив (в своё время уже столкнулся с проблемой не выходящих из большой кучи массивов)?
Как лучше манипулировать элементами в массиве, при изменении размеров изображения, смещению, отражению? (Array.Copy ? Что-то ещё? Создание нового массива против изменения текущего? Использование маленьких буферов (по ширине изображения) или копирование всего массива в 2-3 захода через здоровый буфер?)
Буду благодарен за любые подсказки и помощь!
---
Ответ #1: Спасибо, unsafe на заметку взял.
---
Ответ #2: Спасибо, kooder! Очень ценная информация, буду осваивать. FieldOffset тоже вижу впервые - симпотично (я бы делал битовыми сдвигами). Благодарю!
По расходу памяти два первых вида массива совершенно одинаковы: они выделят память одним сплошным куском. Изрезанный массив выделит память кусками: в итоге на каждый кусок (строку) будут дополнительные затраты (но это такие мелочи, что можно не обращать внимания), зато не нужен один большой кусок! В итоге для очень больших изображений именно изрезанный массив может найти достаточно памяти в системе, в то время как первые два не найдут столь большой непрерывный участок свободной памяти.
Посмотри класс System.Buffer - в MSDN пишется, что он обеспечивает лучшую производительность над простыми типами, чем класс System.Array. Смотри метод Buffer.BlockCopy.
Также посмотри System.ArraySegment - удобная вещь для работы с частью массива. Причём одномерного массива, то есть можно применить и к строке изрезанного массива.
Дык стандартный System.Color - он и хранится как единый int. На каждую составляющую цвета - один байт, и ещё один байт - прозрачность. А уж какую обёртку сделать над int'ом - зависит от целей. Навскидку, я бы сделал так:
struct Color
{
[FieldOffset(0)]
public int color;
[FieldOffset(0)]
public byte alpha;
[FieldOffset(1)]
public byte red;
[FieldOffset(2)]
public byte green;
[FieldOffset(3)]
public byte blue;
}
Работа с большими изображениями требует больших мощностей. Поэтому никаких свойств, индексаторов и т. п.
И да, если планируются тяжёлые вычисления, то крайне желателен unsafe-код. И никакого linq'а.
имеет смысл использовать unsafe код. При больших массивах обработка с использованием арифметики указателей дает (вродебы) двухкратное увеличение производительности, что практически равно нативному коду.
Но там есть ограничения - всю программу так не напишешь.
Стандартные Bitmap'ы хранятся в памяти построчно. В предлагаемых массивах (см. выше) - аналогично. Поэтому, чтобы распараллеливание действительно происходило, писать нужно примерно так:
{
Parallel.For (0, imageWidth, x =>
{
...
});
});
{
Parallel.For (0, imageWidth, x =>
{
...
});
});
А данный код ты привел исключительно для примера или как рабочий? Мне кажется, что попытки распаралетить каждый пиксель картинки будут не очень то эффективны? С внешним циклом - по строкам - согласен, а внутренний, как мне кажется, стоит оставить обыкновенным for...
В частности сейчас описываю метод рисования прямоугольника с использованием заливки:
int maxx = Math.Max(x1, x2);
int miny = Math.Min(y1, y2);
int maxy = Math.Max(y1, y2);
int width = Width;
Parallel.For(miny, maxy,
y =>
{
for (int x = minx; x < maxx; x++)
_colors[y * width + x] = color;
});
Если над пикселом производятся лёгкие операции, то внутренний цикл действительно не стоит параллелить. Но если там будут долгие вычисления, то стоит. Как обычно, всё решают тесты.