Я работал над WPF, который декодирует QR-код, который пользователь держит на веб-камере.Приложение работает довольно хорошо и отлично работает на моем компьютере разработчика (максимальная загрузка процессора 23% и 4% GPU на процессоре Core i7 3770, GPU NVidia Quadro K4200), но когда я установлю и запустлю его на компьютере, он будет использоваться на(Intel NUC NUC7CJYH) Загрузка процессора для приложения составляет> 94%, что приводит к 100% использованию на компьютере.
В настоящее время приложение сканирует QR-код пользователя дважды - первым является QR-код сотрудника,и второй - QR-код для номера проекта, над которым они работали.Приложение использует ZXing.Net для декодирования QR-кодов и AForge.Net для доступа к веб-камере.
Я запустил VS Profiler, и это скриншот с результатами: VS Profiler Output
Согласно результатам VS Profiler, есть 4 вызова метода, которые используют наибольшее количество процессорного времени, и, вероятно, я должен сосредоточиться на:
videoSource_NewFrame: Thisполучает каждый кадр, представленный в VideoCaptureDevice (часть класса AForge.Video.DirectShow), и отображает его в элементе управления Image, называемом imgSource.При этом используется ~ 10,56% от общего времени ЦП (3745 мс)
void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
BitmapImage bi;
using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
{
bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
}
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate { imgSource.Source = bi; }));
}
catch (Exception ex)
{
MessageBox.Show("Error with attaching video frame.\n " + ex.Message);
MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
timer_Tick: этот метод представляет собой тик DispatcherTimer EventHandler и вызывается с интервалом в 1 секунду.Используется для отображения текущего времени в элементе управления lblTime и использует ~ 9,56% процессорного времени (3389 мс)
private void timer_Tick(object sender, EventArgs e)
{
currentTime = new DateTime();
ts = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
currentTime = currentTime.Date + ts;
lblTime.Content = $"{currentTime.ToString("HH:mm")}";
}
qrTimer_Tick: еще один тик DispatcherTimer EventHandler.Этот метод вызывается каждые 3 секунды и инициирует сканирование для QR-кода в каждом интервале.Он использует ~ 5,52% (1959 мс)
try
{
MemoryStream memoryStream = new MemoryStream();
var encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(imgSource.Source as System.Windows.Media.Imaging.BitmapSource));
encoder.Save(memoryStream);
memoryStream.Flush();
capturedImage = (Bitmap)System.Drawing.Image.FromStream(memoryStream);
BarcodeReader reader = new BarcodeReader();
reader.AutoRotate = true;
reader.TryInverted = true;
reader.Options = new DecodingOptions { TryHarder = true };
if (capturedImage != null)
result = reader.Decode(capturedImage);
if (result != null)
{
if (isStep1 == true)
{
QRScanUserID();
}
else if (isStep1 == false & isStep2 == true)
{
QRScanProjectID();
}
}
}
catch (Exception)
{
MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
Внешний вызов метода Decode в ZXing.Net.При этом используется 5,34% от общего процессорного времени (1895 мс)
Я не уверен, как можно оптимизировать код.Я посмотрел на настройку DispactherPriority для каждого из двух DispatcherTimers, но при тестировании это не влияет на использование процессора приложением и влияет на интервалы каждого DispatcherTimer.Я также попытался изменить 3 параметра BarcodeReader, которые я настроил в методе qrTimer_Tick, на false, что дало улучшение примерно на 1%.Тем не менее, я бы предположил, что это незначительно, так как это зависит от таких факторов, как то, как быстро пользователь помещает QR-код перед камерой, и если qrTimer_Tick уже сработал.
Что-то мне не хватает?Может ли быть так, что приложение перегружает машину NUC, на которой оно работает?
EDIT После того, как я следовал рекомендациям kennyzx и lerthe61, мне удалось получить загрузку процессора приложения на NUC NUC7CJYH.до ~ 58% в пике.Самое большое преимущество заключалось в удалении объекта DispatcherTimer, который вызывал обработчик событий timer_Tick каждую секунду.Запуск VS Profiler вскоре после того, как я сделал это изменение, показал пиковую загрузку ЦП на моем компьютере разработчика в размере 18%, что произошло вскоре после запуска, когда приложение начало работать.
После ввода данных lerthe61 у меня тогда былопосмотрите на мое использование объектов MemoryStream и Bitmap.Метод videoSource_NewFrame теперь выглядит следующим образом:
void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
BitmapImage bi;
capturedImage = (Bitmap)eventArgs.Frame.Clone();
bi = new BitmapImage();
bi.BeginInit();
ms = new MemoryStream();
capturedImage.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate { imgSource.Source = bi; }));
}
catch (Exception ex)
{
MessageBox.Show("Error with attaching video frame.\n " + ex.Message);
MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
Я использовал блокировку в методе qrTimer_Tick для блокировки текущего захваченного изображения, поступающего с веб-камеры:
if (capturedImage != null)
{
lock (_qrTimerLock)
{
result = reader.Decode(capturedImage);
}
}
Несмотря на мое использованиеблокировка, приложение иногда выдает ошибку «Объект в настоящее время используется в другом месте».Мой рефакторинг кода мало повлиял на эффективность использования процессора приложением, вместо удаления timer_Tick, поэтому я мог оставить его как есть, однако это, очевидно, менее эффективно.