Оптимизация приложения WPF QR Decoding, которое использует ~ 100% загрузки процессора на Intel NUC NUC7CJYH - PullRequest
0 голосов
/ 06 сентября 2018

Я работал над 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, поэтому я мог оставить его как есть, однако это, очевидно, менее эффективно.

1 Ответ

0 голосов
/ 02 октября 2018

Я решил остановиться на удалении DispatcherTimer, который запускался каждую секунду, и оставил второй объект DispatcherTimer, который запускается каждые 3 секунды. Производительность приложения является благоприятной, используя только это исправление.

Спасибо всем за помощь!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...