Использование WriteableBitmap будет самым быстрым подходом. Для тестирования вы можете предварительно выделить массив и использовать секундомер для выборки таймингов по мере рендеринга, затем вы можете проанализировать тайминги, чтобы получить представление о производительности.
Одна из основных проблем, которые у вас есть, - это сбор мусора. Это, к сожалению, создаст потенциал для точного типа проблем с производительностью, которые вы описываете, например, случайное застопоривание, в то время как GC выполняется. Вы можете экспериментировать с GC с низкой задержкой, чтобы смягчить это.
Update
Ниже приведен пример использования низкой GC латентности:
http://blogs.microsoft.co.il/blogs/sasha/archive/2008/08/10/low-latency-gc-in-net-3-5.aspx
Вы можете использовать это, чтобы гарантировать, что нет никаких коллекций мусора во время вашего «мертвого времени», то есть рендеринга время.
Update 2
Как я уже говорил в моем комментарии некоторое время назад - вы дозированием обновления вашего WritableBitmap?
Частота обновления вашего устройства слишком велика, чтобы поддерживать запись в растровое изображение для каждого обновления устройства. Я думаю, что есть 10k-100k обновлений в секунду. Попробуйте обновить растровое изображение на более чувствительной частоте (например, 60 или 25 раз в секунду), поскольку накладные расходы на форматирование рендеринга растровых изображений будут доминировать при производительности со скоростью 10 кБ в секунду в секунду. Записывайте в буфер при получении обновлений устройства, а затем периодически переносите этот буфер на WritableBitmap. Вы можете использовать для этого таймер или делать это каждый раз, когда обновляется устройство. Таким образом, вы будете загружать свои обновления и значительно уменьшать накладные расходы WritableBitmap.
Update 3
Хорошо, это звучит, как вы обновляете WritableBitmap раз 10k-100k в секунду - это не представляется возможным. Попробуйте использовать механизм frame \ batch, как описано ранее. Также ваш дисплей, скорее всего, будет обновляться со скоростью 60 кадров в секунду.
Если вы обеспокоены блокировкой обновлений своего устройства, рассмотрите возможность использования двух чередующихся задних буферов и многопоточности. Таким образом, вы периодически переключаете, какой буфер обратно записывает ваше устройство, и используйте второй поток для визуализации обмениваемого буфера в WritableBitmap. Пока вы можете поменять буфер в < 10 мкс, вы можете сделать это в мертвое время, не блокируя обновления своего устройства.
Update 4
Далее в ответ на мой вопрос, казалось бы, что в настоящее время «блокировка \ разблокировка» вызывается для каждого из 100k обновлений в секунду. Это то, что, вероятно, приводит к гибели. На моей (мощной) системе я измерил 100k «lock \ unlock» на ~ 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);
}
Можете ли вы быть немного более конкретно: что такое «много»? И пиксель действительно подходящий «прямоугольник» для вас? спасибо –
@Simon: Конечно! Я ожидаю, возможно, от 10.000 до 100.000 точек данных в секунду. Одно измерение не превышает нескольких секунд.Пикселы достаточно прямоугольны для меня =) – Jens
@Jens Что касается вашего обновления, я предложил использовать второй поток для переноса данных буфера в WritableBitmap, чтобы точно решить проблему, которую вы описываете. –