Использование WriteableBitmap будет самым быстрым подходом.Для тестирования вы можете предварительно выделить массив и использовать секундомер для выборки таймингов при рендеринге, затем вы можете проанализировать тайминги, чтобы получить представление о производительности.
Одна из ваших основных проблем связана со сборкой мусора.,К сожалению, это создаст потенциальные возможности для проблем с производительностью, которые вы описываете, например, случайное зависание во время проведения GC.Вы можете поэкспериментировать с GC с низкой задержкой, чтобы смягчить это.
Update
Вот пример использования GC с низкой задержкой:
http://blogs.microsoft.co.il/blogs/sasha/archive/2008/08/10/low-latency-gc-in-net-3-5.aspx
Вы можете использовать это, чтобы гарантировать, что во время «мертвого времени», то есть времени рендеринга, не будет никаких сборок мусора.
Обновление 2
Как я уже говорилв моем комментарии некоторое время назад - вы пакетируете обновления для вашего WritableBitmap?
Частота обновления вашего устройства слишком высока, чтобы выдерживать запись в растровое изображение для каждого обновления устройства - я думаю, что в каждом обновлении находится от 10 до 100 000 обновлений.второй.Попробуйте обновить свое растровое изображение на более заметной частоте (например, 60 или 25 раз в секунду), поскольку накладные расходы на принудительное отображение растрового изображения будут доминировать при производительности при 10–100 тыс. Обновлений в секунду.Выполняйте запись в буфер при получении обновлений устройства, а затем периодически переносите этот буфер в WritableBitmap.Вы можете использовать таймер для этого или делать это каждые n обновлений устройства.Таким образом, вы будете пакетировать свои обновления и значительно сократить накладные расходы при рендеринге WritableBitmap.
Обновление 3
Хорошо, похоже, что вы обновляете WritableBitmap 10–100 тыс. Раз завторое - это нереально.Пожалуйста, попробуйте механизм, основанный на кадрах и пакетах, как описано ранее.Кроме того, ваш дисплей может обновляться с частотой 60 кадров в секунду.
Если вас беспокоит блокировка обновлений вашего устройства, рассмотрите возможность использования двух чередующихся обратных буферов и многопоточности.Таким образом, вы периодически переключаетесь в какой буфер буферизует ваше устройство и используете второй поток для рендеринга замененного буфера в WritableBitmap.Пока вы можете поменять буфер в <10 мкс, вы можете делать это в мертвое время, не блокируя обновления вашего устройства. </p>
Обновление 4
В дополнение к ответуна мой вопрос, может показаться, что в настоящее время вызывается «блокировка \ разблокировка» для каждого из 100 000 обновлений в секунду.Это то, что, вероятно, убивает производительность.На моей (мощной) системе я измерил 100k "блокировка \ разблокировка" на ~ 275мс.Это довольно тяжело и будет намного хуже в системах с низким энергопотреблением.
Вот почему я думаю, что 100 тыс. Обновлений в секунду недостижимо, т.е. блокировка -> обновление -> разблокировка.Блокировка слишком дорогая.
Вам нужно найти способ снизить количество блокирующих вызовов, не блокируя их вообще, блокируя каждые n операций или, возможно, пакетные запросы и затем применяя пакетное обновление взамок.Здесь есть несколько вариантов.
Если вы хотите выполнить пакетное обновление, оно может составить всего 10 циклов, что снизит частоту обновления до 10 000 обновлений в секунду.Это сократит ваши накладные расходы на блокировку в 10 раз.
Пример кода эталонного теста для блокировки накладных расходов на вызовах 100k:
lock/unlock - Interval:1 - :289.47ms
lock/unlock - Interval:1 - :287.43ms
lock/unlock - Interval:1 - :288.74ms
lock/unlock - Interval:1 - :286.48ms
lock/unlock - Interval:1 - :286.36ms
lock/unlock - Interval:10 - :29.12ms
lock/unlock - Interval:10 - :29.01ms
lock/unlock - Interval:10 - :28.80ms
lock/unlock - Interval:10 - :29.35ms
lock/unlock - Interval:10 - :29.00ms
Код:
public void MeasureLockUnlockOverhead()
{
const int TestIterations = 5;
Action<string, Func<double>> test = (name, action) =>
{
for (int i = 0; i < TestIterations; i++)
{
Console.WriteLine("{0}:{1:F2}ms", name, action());
}
};
Action<int> lockUnlock = interval =>
{
WriteableBitmap bitmap =
new WriteableBitmap(100, 100, 96d, 96d, PixelFormats.Bgr32, null);
int counter = 0;
Action t1 = () =>
{
if (++counter % interval == 0)
{
bitmap.Lock();
bitmap.Unlock();
}
};
string title = string.Format("lock/unlock - Interval:{0} -", interval);
test(title, () => TimeTest(t1));
};
lockUnlock(1);
lockUnlock(10);
}
[SuppressMessage("Microsoft.Reliability",
"CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static double TimeTest(Action action)
{
const int Iterations = 100 * 1000;
Action gc = () =>
{
GC.Collect();
GC.WaitForFullGCComplete();
};
Action empty = () => { };
Stopwatch stopwatch1 = Stopwatch.StartNew();
for (int j = 0; j < Iterations; j++)
{
empty();
}
double loopElapsed = stopwatch1.Elapsed.TotalMilliseconds;
gc();
action(); //JIT
action(); //Optimize
Stopwatch stopwatch2 = Stopwatch.StartNew();
for (int j = 0; j < Iterations; j++)
{
action();
}
gc();
double testElapsed = stopwatch2.Elapsed.TotalMilliseconds;
return (testElapsed - loopElapsed);
}