Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Обёртка над массивом 2^26 байт. Оптимизация

9.7K
24 июня 2012 года
Vitamant
228 / / 07.02.2011
Доброго времени суток!

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 тоже вижу впервые - симпотично (я бы делал битовыми сдвигами). Благодарю!
297
24 июня 2012 года
koodeer
1.2K / / 02.05.2009
Цитата:
что эффективнее - Color[], Color[,] или Color[][] ?


По расходу памяти два первых вида массива совершенно одинаковы: они выделят память одним сплошным куском. Изрезанный массив выделит память кусками: в итоге на каждый кусок (строку) будут дополнительные затраты (но это такие мелочи, что можно не обращать внимания), зато не нужен один большой кусок! В итоге для очень больших изображений именно изрезанный массив может найти достаточно памяти в системе, в то время как первые два не найдут столь большой непрерывный участок свободной памяти.

Цитата:
Array.Copy ? Что-то ещё?


Посмотри класс System.Buffer - в MSDN пишется, что он обеспечивает лучшую производительность над простыми типами, чем класс System.Array. Смотри метод Buffer.BlockCopy.
Также посмотри System.ArraySegment - удобная вещь для работы с частью массива. Причём одномерного массива, то есть можно применить и к строке изрезанного массива.

Цитата:
Или вообще стоит хранить их не цветами, а int'ами?


Дык стандартный System.Color - он и хранится как единый int. На каждую составляющую цвета - один байт, и ещё один байт - прозрачность. А уж какую обёртку сделать над int'ом - зависит от целей. Навскидку, я бы сделал так:

Код:
[StructLayout(LayoutKind.Explicit)]
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'а.
40K
24 июня 2012 года
D129
228 / / 18.04.2012
Единственное что читал на эту тему - что при обработке изображений в дотнет
имеет смысл использовать unsafe код. При больших массивах обработка с использованием арифметики указателей дает (вродебы) двухкратное увеличение производительности, что практически равно нативному коду.

Но там есть ограничения - всю программу так не напишешь.
297
24 июня 2012 года
koodeer
1.2K / / 02.05.2009
Да, вот ещё что. Многоядерность в наше время - норма. Поэтому распараллеливать обработку изображений - тоже норма. Витаманта, думаю, учить не нужно, а вот если новичок в тему заглянет, то дам совет по правильному порядку обработки пикселей.
Стандартные Bitmap'ы хранятся в памяти построчно. В предлагаемых массивах (см. выше) - аналогично. Поэтому, чтобы распараллеливание действительно происходило, писать нужно примерно так:
 
Код:
Parallel.For(0, imageHeight, y =>
{
    Parallel.For (0, imageWidth, x =>  
    {
        ...
    });
});
То есть внешний цикл - по координате y, внутренний - по x. Тогда система разобьёт изображение по строкам, и они будут обработаны на отдельных ядрах. Если написать наоборот, то эффективного распараллеливания не получится.
9.7K
24 июня 2012 года
Vitamant
228 / / 07.02.2011
Цитата: koodeer
 
Код:
Parallel.For(0, imageHeight, y =>
{
    Parallel.For (0, imageWidth, x =>  
    {
        ...
    });
});
То есть внешний цикл - по координате y, внутренний - по x. Тогда система разобьёт изображение по строкам, и они будут обработаны на отдельных ядрах. Если написать наоборот, то эффективного распараллеливания не получится.


А данный код ты привел исключительно для примера или как рабочий? Мне кажется, что попытки распаралетить каждый пиксель картинки будут не очень то эффективны? С внешним циклом - по строкам - согласен, а внутренний, как мне кажется, стоит оставить обыкновенным for...
В частности сейчас описываю метод рисования прямоугольника с использованием заливки:

Код:
int minx = Math.Min(x1, x2);
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;
                 });
Кстати, хочу заметить, что данная конструкция (вначале по строкам, затем по столбцам, а не наоборот) эффективна будет в виду использования процессорами механизмов "предсказывания". И при переборе соседних значений (x) вероятность предсказания (и более быстрой выборки) будет существенно выше, чем при перескакивании между ячейками находящимися (в лучшем случае) на расстоянии ширины изображения друг от друга. Хотя в конечном итоге всё зависит от конкретного процессора.
297
24 июня 2012 года
koodeer
1.2K / / 02.05.2009
Цитата:
А данный код ты привел исключительно для примера или как рабочий? Мне кажется, что попытки распаралетить каждый пиксель картинки будут не очень то эффективны? С внешним циклом - по строкам - согласен, а внутренний, как мне кажется, стоит оставить обыкновенным for...


Если над пикселом производятся лёгкие операции, то внутренний цикл действительно не стоит параллелить. Но если там будут долгие вычисления, то стоит. Как обычно, всё решают тесты.

5
28 июня 2012 года
hardcase
4.5K / / 09.08.2005
На всякий случай: если массив не влазит в оперативную память, то можно использовать MemoryMappedFile.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог