Производительность приложений Xamarin со временем снижается, что в итоге приводит к сбою - PullRequest
0 голосов
/ 07 марта 2019

В настоящее время я разрабатываю приложение Xamarin, которое взаимодействует через соединение Wi-Fi с некоторым нестандартным оборудованием, содержащим датчик. Работа моего приложения состоит в том, чтобы получать данные датчика с аппаратного обеспечения и отображать их с помощью oxyplot в виде непрерывного потока 1-D данных. К сожалению, я заметил, что производительность моего приложения со временем обычно падает, и в какой-то момент он всегда падает. Точное время сбоя совершенно произвольно, но, похоже, зависит от количества данных датчика (то есть большие значения амплитуды приводят к более быстрым сбоям приложения). Ниже приведены некоторые типичные выходные данные отладки до сбоя приложения:

03-07 17:36:54.149 I/art     (10777): Starting a blocking GC Explicit
03-07 17:36:54.164 I/art     (10777): Explicit concurrent mark sweep GC freed 2991(107KB) AllocSpace objects, 0(0B) LOS objects, 39% free, 10MB/17MB, paused 174us total 14.883ms
03-07 17:36:54.168 D/Mono    (10777): GC_TAR_BRIDGE bridges 1104 objects 1104 opaque 0 colors 1104 colors-bridged 1104 colors-visible 1104 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.06ms tarjan 0.28ms scc-setup 0.17ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.14ms
03-07 17:36:54.168 D/Mono    (10777): GC_BRIDGE: Complete, was running for 20.42ms
03-07 17:36:54.168 D/Mono    (10777): GC_MINOR: (Nursery full) time 3.34ms, stw 5.17ms promoted 211K major size: 3664K in use: 2807K los size: 14336K in use: 11300K
03-07 17:36:56.248 I/art     (10777): Starting a blocking GC Explicit
03-07 17:36:56.263 I/art     (10777): Explicit concurrent mark sweep GC freed 3093(111KB) AllocSpace objects, 0(0B) LOS objects, 39% free, 10MB/17MB, paused 180us total 14.729ms
03-07 17:36:56.267 D/Mono    (10777): GC_TAR_BRIDGE bridges 1144 objects 1144 opaque 0 colors 1144 colors-bridged 1144 colors-visible 1144 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.07ms tarjan 0.30ms scc-setup 0.18ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.13ms
03-07 17:36:56.267 D/Mono    (10777): GC_BRIDGE: Complete, was running for 21.07ms
03-07 17:36:56.267 D/Mono    (10777): GC_MINOR: (Nursery full) time 3.60ms, stw 4.38ms promoted 216K major size: 3904K in use: 3055K los size: 18432K in use: 14810K
03-07 17:36:56.962 D/Mono    (10777): GC_BRIDGE waiting for bridge processing to finish
03-07 17:36:56.968 I/art     (10777): Starting a blocking GC Explicit
03-07 17:36:56.988 I/art     (10777): Explicit concurrent mark sweep GC freed 1327(43KB) AllocSpace objects, 0(0B) LOS objects, 40% free, 10MB/17MB, paused 220us total 19.794ms
03-07 17:36:56.992 D/Mono    (10777): GC_TAR_BRIDGE bridges 648 objects 649 opaque 0 colors 648 colors-bridged 648 colors-visible 648 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.11ms tarjan 0.29ms scc-setup 0.16ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.13ms
03-07 17:36:56.992 D/Mono    (10777): GC_BRIDGE: Complete, was running for 25.65ms
03-07 17:36:56.992 D/Mono    (10777): GC_MAJOR: (LOS overflow) time 16.50ms, stw 21.50ms los size: 5120K in use: 460K
03-07 17:36:56.992 D/Mono    (10777): GC_MAJOR_SWEEP: major size: 3984K in use: 2657K
03-07 17:36:59.675 I/Choreographer(10777): Skipped 116 frames!  The application may be doing too much work on its main thread.
03-07 17:37:01.298 I/art     (10777): Starting a blocking GC Explicit
03-07 17:37:01.304 D/Mono    (10777): GC_BRIDGE waiting for bridge processing to finish
03-07 17:37:01.314 I/art     (10777): Explicit concurrent mark sweep GC freed 3078(111KB) AllocSpace objects, 0(0B) LOS objects, 40% free, 10MB/17MB, paused 173us total 15.393ms
03-07 17:37:01.319 D/Mono    (10777): GC_TAR_BRIDGE bridges 1144 objects 1144 opaque 0 colors 1144 colors-bridged 1144 colors-visible 1144 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.07ms tarjan 0.33ms scc-setup 0.20ms gather-xref 0.02ms xref-setup 0.00ms cleanup 0.40ms
03-07 17:37:01.319 D/Mono    (10777): GC_BRIDGE: Complete, was running for 23.28ms
03-07 17:37:01.319 D/Mono    (10777): GC_MINOR: (Nursery full) time 3.76ms, stw 5.02ms promoted 216K major size: 4128K in use: 2905K los size: 5120K in use: 4050K
03-07 17:37:03.143 I/art     (10777): Starting a blocking GC Explicit
03-07 17:37:03.158 I/art     (10777): Explicit concurrent mark sweep GC freed 3070(110KB) AllocSpace objects, 0(0B) LOS objects, 40% free, 10MB/17MB, paused 252us total 14.860ms
03-07 17:37:03.161 D/Mono    (10777): GC_TAR_BRIDGE bridges 1134 objects 1134 opaque 0 colors 1134 colors-bridged 1134 colors-visible 1134 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.07ms tarjan 0.30ms scc-setup 0.18ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.13ms
03-07 17:37:03.161 D/Mono    (10777): GC_BRIDGE: Complete, was running for 20.60ms
03-07 17:37:03.161 D/Mono    (10777): GC_MINOR: (Nursery full) time 3.40ms, stw 4.31ms promoted 216K major size: 4256K in use: 3153K los size: 9216K in use: 7680K
03-07 17:37:05.200 I/art     (10777): Starting a blocking GC Explicit
03-07 17:37:05.216 I/art     (10777): Explicit concurrent mark sweep GC freed 3008(108KB) AllocSpace objects, 0(0B) LOS objects, 39% free, 10MB/17MB, paused 182us total 15.827ms
03-07 17:37:05.219 D/Mono    (10777): GC_TAR_BRIDGE bridges 1110 objects 1110 opaque 0 colors 1110 colors-bridged 1110 colors-visible 1110 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.07ms tarjan 0.32ms scc-setup 0.19ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.16ms
03-07 17:37:05.219 D/Mono    (10777): GC_BRIDGE: Complete, was running for 21.94ms
03-07 17:37:05.219 D/Mono    (10777): GC_MINOR: (Nursery full) time 3.70ms, stw 4.93ms promoted 208K major size: 4416K in use: 3393K los size: 13312K in use: 11258K
Thread finished: <Thread Pool> #4
Thread finished: <Thread Pool> #2
Thread started: <Thread Pool> #10
Thread finished: <Thread Pool> #3
Thread finished:  #11

Это метод, содержащий основной цикл, в котором я обновляю график:

public static async Task MainMethodAsync(MyDisplay MyDisplayPlot, DateTime StartTime, MainPage mainPage) {
    ToggleAccelerometer();
    Accelerometer.ReadingChanged += Accelerometer_ReadingChanged;
    Client = new MyClientSocket();
    await Client.ConnectAndStartReadTaskAsync();
    int FrameCounter = 0;
    while (ShootContinuously) {
        FrameCounter++;
        await GetDataAndUpdateUIAsync(MyDisplayPlot,FrameCounter,StartTime,mainPage);
    }
    await Client.DisconnectAndStopReadTaskAsync();
}

Метод, вызываемый на каждой итерации, приведен ниже:

private static async Task GetDataAndUpdateUIAsync(MyDisplay MyDisplayPlot, int FrameCounter, DateTime StartTime, MainPage mainPage) {
    ShootAsync();
    await ReceiveAsync();
    if (PlottingEnabledFlag) {
        MyDisplayPlot.UpdateMyDisplayPlot();
    }
    DateTime CurrentTime = DateTime.Now;
    double elapsedSecs = (CurrentTime - StartTime).TotalSeconds;
    mainPage.Title = "Frames: " + FramesCounter.ToString() + ", Frames/s: " + System.Math.Round((FramesCounter / elapsedSecs), 3).ToString();
}

Что я уже пробовал?

  1. Моим первым предположением было то, что виновником мог быть oxyplot, поэтому я ввел флаг, с помощью которого я могу отключить отображение своих данных. К сожалению, приложение в какой-то момент все еще падает, даже если данные вообще не отображаются.
  2. В StackOverflow I читайте , что «проблема может быть в том, что« Пропущено XX кадров! Приложение может выполнять слишком много работы в своем основном потоке ». Поэтому я решил использовать метод RunOnUiThread, но это просто привело к тому, что данные вообще не обрабатывались.
  3. Другая идея состояла в том, чтобы ввести тайм-аут для асинхронного метода. Это работает нормально, но имеет тот недостаток, что через некоторое время порог тайм-аута достигает каждой итерации, и приложение, похоже, не возвращается к «обычному бизнесу».

Любые дополнительные идеи горячо приветствуются, и любой вклад высоко ценится. Спасибо.

Обновление

Вот как выглядит основной метод после введения некоторой логики времени ожидания и очистки:

    [...]
    int timeout = 1000;
    while (ShootContinuously) {
        FrameCounter++;
        CancellationToken cancellationToken = new CancellationToken();
        var task = GetDataAndUpdateUIForContinuousShootingAsync(MyDisplayPlot, FrameCounter, StartTime, mainPage);
        if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task) {
            // Task completed within timeout.
            // Consider that the task may have faulted or been canceled.
            // We re-await the task so that any exceptions/cancellation is rethrown.
            await task;
        } else { // timeout/cancellation logic
            Debug.WriteLine("Task is taking too long!");
            await Client.DisconnectAndStopReadTaskAsync();
            Client = null;
            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Client = new MyClientSocket();
            await Client.ConnectAndStartReadTaskAsync();
            continue;
        }
    }
    await Client.DisconnectAndStopReadTaskAsync();
...