Сильно различающиеся быстродействие цикла, с отладкой и без

Рейтинг: 1Ответов: 0Опубликовано: 02.06.2023

Минимальный код для воспроизведения проблемы.

  internal partial class Win32WPFWindowPositionUpdater
    {
        private readonly Window _window;
        private readonly Dispatcher _dispatcher;
        private const int IGNORE_SIZE_WINDOW = -1;
        private const int HWND_TOP = 0;
        private const int NOT_MESSAGE_WM_WINDOWPOSCHANGING = 0x0400;
        private const int SWP_NOSIZE = 0x0001;
        private const int OFFSET_CURSOR = 30;
        public Win32WPFWindowPositionUpdater(Window window)
        {
            _window = window;
            _dispatcher = _window.Dispatcher;
        }
        public Task UpdateWindowPos(CancellationToken token) => UpdateWindowPositionRelativeToCursor(token);
        [LibraryImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static partial bool SetWindowPos(IntPtr handle, int handle2, int x, int y, int cx, int cy, int flag);
        private async Task UpdateWindowPositionRelativeToCursor(CancellationToken token)
        {
            WindowInteropHelper helper = new WindowInteropHelper(_window);
            while (token.IsCancellationRequested is not true)
            {
                await Task.Delay(1, token);

                if (token.IsCancellationRequested is true) return;
                Point point = GetCursorPosition();
                await _window.Dispatcher.InvokeAsync(() =>
                {
                    SetWindowPos(helper.Handle, HWND_TOP, Convert.ToInt32(point.X - OFFSET_CURSOR), Convert.ToInt32(point.Y - OFFSET_CURSOR),
                        IGNORE_SIZE_WINDOW, IGNORE_SIZE_WINDOW, NOT_MESSAGE_WM_WINDOWPOSCHANGING | SWP_NOSIZE);
                }, System.Windows.Threading.DispatcherPriority.Render, token);
            }
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public static implicit operator Point(POINT point)
            {
                return new Point(point.X, point.Y);
            }
        }
        [LibraryImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool GetCursorPos(out POINT lpPoint);
        internal static Point GetCursorPosition()
        {
            GetCursorPos(out POINT lpPoint);
            return lpPoint;
        }
    }
}
  public partial class MainWindow : Window
    {
        private readonly Win32WPFWindowPositionUpdater  _positionUpdater;
        private readonly CancellationTokenSource _tokenSource;

        public MainWindow()
        {
            InitializeComponent();

            _positionUpdater = new Win32WPFWindowPositionUpdater(this);

            _tokenSource = new CancellationTokenSource();

            _ = new Timer(new TimerCallback((_) =>
            {
                _positionUpdater.UpdateWindowPos(_tokenSource.Token);
            }), null, 1500, 0);

            _ = new Timer(new TimerCallback((_) =>
            {
                _tokenSource.Cancel();
            }), null, 7000, 0);
        }
    }

Визуально видно, что окно без отладки обновляет позицию окна медленнее. Если точнее, то вычисленная мною задержка между итерациями цикла обновления окна падает до 29% от изначальной. Причем на это влияет опция "горячая перезагрузка XAML", при её отключении в настройках Visual Studio. Скорость выполнения также падает.

Ожидаемое поведение - незначительные отклонения скорости выполнения цикла между режимами, с отладкой и без.

Это воспроизводимое поведение, или это проблемы на моём локальном компьютере? Если воспроизводимое, то по возможности разобраться в причинах, и устранить разницу.

UPDATE:

Будет выполнятся около 15 секунд.

 internal class Program
    {
        static async Task Main()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            await MyTask();
            stopwatch.Stop();
            await Console.Out.WriteLineAsync($"{stopwatch.Elapsed}");
        }

        private static async Task<int> MyTask()
        {
          
            int count = 0;
            while (count < 1000)
            {
                await Task.Delay(1);
                count++;
                await Console.Out.WriteLineAsync($"{count}");
            }
            return count;
        }
    }

Это около 2-3 секунд.

 internal class Program
    {
        static async Task Main()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            await MyTask();
            stopwatch.Stop();
            await Console.Out.WriteLineAsync($"{stopwatch.Elapsed}");
        }

        private static async Task<int> MyTask()
        {
            Thread thread = new Thread(() =>
            {
                System.Windows.Window window = new System.Windows.Window();
         
                System.Windows.Application application = new System.Windows.Application();
                application.Run(window);
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();

            int count = 0;
            while (count < 1000)
            {
                await Task.Delay(1);
                count++;
                await Console.Out.WriteLineAsync($"{count}");
            }
            return count;
        }
    }

Когда NET 7 и "горячая перезагрузка" включены. Потому что приложение запросило таймер при await Task.Delay(1) в этом случаи, в отличии от первого. Убедится в этом можно прописав powercfg -energy duration 1 в консоль, прочить файл отчёта. Отчёт

UPDATE: Оказывается недостаточно просто указать await Task.Delay(1), на самом деле в большинстве случаев будет просто использоваться значение в "15.625 ms". интервал

Чтобы использовать меньшие величины нужен явный вызов timeBeginPeriod

P.S. В принце об этой задержки невнятно в документации по Task.Dalay() написано.

Ответы

Ответов пока нет.