Guía introductoria de configuración de CPU para desarrolladores de aplicaciones Windows: prioridad, afinidad y núcleos P/E

· Actualizado el: · · Windows, Aplicaciones Windows, CPU, Rendimiento, Prioridad, Afinidad, Núcleos P, Núcleos E, Ahorro de energía, EcoQoS

El rendimiento de una aplicación Windows no depende solo del código. Con el mismo .exe, la sensación de uso puede cambiar según el equipo en el que se ejecute.

En un PC, el procesamiento periódico es estable.
En otro, a veces se retrasa.
Con alimentación de CA no hay problema, pero con batería va extrañamente lento.
Se sube la prioridad en el Administrador de tareas, pero no se acelera tanto como se esperaba.
Se intenta favorecer los núcleos P, pero el tiempo de proceso sigue fluctuando.
Y al contrario, cuando se intenta mejorar el rendimiento, el ventilador no para de girar y otras aplicaciones se vuelven lentas.

Al desarrollar aplicaciones Windows es habitual encontrarse con este tipo de fenómenos difíciles de explicar solo con el código.

En esos momentos conviene mirar estas cuatro cosas.

Qué mirar En pocas palabras
Prioridad Qué subproceso se ejecuta primero
Afinidad En qué CPU puede ejecutarse
Núcleos P / Núcleos E Si esa CPU está orientada al rendimiento o al ahorro de energía
Configuración de ahorro de energía Cuánto se exige realmente a la CPU

Además, en el Windows actual también intervienen EcoQoS y el Efficiency mode del Administrador de tareas.

Es decir, el entorno de ejecución de una aplicación Windows no se reduce a si «la CPU es rápida o lenta».

Cuándo se ejecuta
Dónde se ejecuta
En qué tipo de núcleo se ejecuta
En qué estado de rendimiento está funcionando la CPU
Si el sistema operativo la considera "orientada al rendimiento" o "puede ir en modo de ahorro de energía"

La combinación de todo esto determina la capacidad de respuesta y el tiempo de proceso reales.

Este artículo organiza la relación entre la prioridad, la afinidad, los núcleos P/E y la configuración de ahorro de energía, aspectos que suelen pasarse por alto en el desarrollo de aplicaciones Windows.

El código que aparece en este artículo se publica en GitHub como un conjunto de muestras que se pueden compilar y ejecutar (una biblioteca y una demostración en C# que gestionan prioridad y afinidad, scripts de PowerShell y pruebas unitarias).

windows-app-cpu-priority-affinity-power - komurasoft-blog-samples (GitHub)

1. Primero, la visión de conjunto

Para empezar, la visión de conjunto se puede representar así.

Relación entre prioridad, afinidad, QoS, núcleos P/E y modo de energía en el planificador de WindowsDiagrama que muestra cómo el código de la aplicación llega al planificador de Windows a través de la prioridad, la afinidad o CPU Sets y el QoS o EcoQoS, cómo el planificador elige el subproceso y el procesador lógico de ejecución, cómo los núcleos P/E y el modo de energía influyen en esa elección, y cómo el resultado afecta la capacidad de respuesta, el tiempo de proceso, el calentamiento, el consumo de batería y el impacto en otras aplicacionesCódigo de la aplicaciónPlanificador de WindowsPrioridadCuándo es más probable que se ejecuteAfinidad / CPU SetsEn qué CPU puede ejecutarseQoS / EcoQoSOrientado al rendimiento o al ahorro de energíaElige el subproceso a ejecutarElige el procesador lógico de ejecuciónNúcleos P / Núcleos EOrientados al rendimiento o al ahorro de energíaModo de energía / Plan de energía / PPMFrecuencia, boost, Core ParkingCapacidad de respuesta realTiempo de procesoCalentamientoConsumo de bateríaImpacto en otras aplicaciones

Figura 1: relación entre la prioridad, la afinidad/CPU Sets, el QoS/EcoQoS, los núcleos P/E y el modo de energía en el planificador de Windows.

Lo importante es que estos elementos no son independientes.

Si se sube la prioridad, es más probable que el subproceso se ejecute.
Pero si la propia CPU está controlada de forma orientada al ahorro de energía, puede que no se acelere tanto como se esperaba.

Si se restringe la CPU con afinidad, puede que se reduzca el movimiento del subproceso entre núcleos.
Pero si el destino elegido es un núcleo orientado al ahorro de energía, o si no encaja con el Core Parking o la gestión de energía, el resultado puede ser desfavorable.

Si se favorecen los núcleos P, el cálculo puede volverse más rápido.
Pero si todo se orienta al lado de alto rendimiento, aumentan el calentamiento, el ruido del ventilador, el consumo de batería y el impacto en otros procesos.

El ajuste de rendimiento de una aplicación Windows no es simplemente «buscar el botón que la hace más rápida».

Qué proceso se quiere acelerar.
Qué proceso puede ser lento sin problema.
Si se prioriza la capacidad de respuesta a la interacción del usuario.
Si se prioriza el tiempo de finalización del procesamiento en segundo plano.
Hasta qué punto se tolera la batería o el calentamiento.

Se trata de esa clase de decisiones de diseño.

2. La prioridad determina “cuándo es más probable que se ejecute”

En Windows, cuando hay varios subprocesos ejecutables, el planificador decide «cuál se pone a continuación en la CPU». Aquí es donde entra en juego la prioridad.

En términos generales, la prioridad se determina en dos niveles.

Clase de prioridad del proceso
  + Prioridad relativa del subproceso
  = Prioridad base del subproceso

En la API Win32, el lado del proceso tiene SetPriorityClass y el lado del subproceso tiene SetThreadPriority.

Para ver la prioridad de un proceso en ejecución con PowerShell, por ejemplo:

Get-Process -Id $PID | Select-Object Id, ProcessName, PriorityClass

Para subir la prioridad del proceso de PowerShell actual, sería así:

$p = Get-Process -Id $PID
$p.PriorityClass = "AboveNormal"

En C# se puede escribir de la siguiente manera:

using System.Diagnostics;

using var process = Process.GetCurrentProcess();
process.PriorityClass = ProcessPriorityClass.AboveNormal;

Aquí conviene tener en cuenta que la prioridad no es «una configuración que hace la CPU más rápida». Lo que determina la prioridad es, cuando hay contención, qué subproceso se ejecuta primero.

No es una configuración que suba la frecuencia de la CPU.
Tampoco es una configuración que elija un núcleo P.
Tampoco es una configuración que acelere la E/S.
Tampoco es una configuración que elimine la espera de bloqueos o de red.

Por eso es habitual que «se subió la prioridad pero no se aceleró». Por ejemplo, si el origen de la lentitud es alguno de estos, subir la prioridad no resuelve el problema de fondo.

  • Espera de E/S de disco
  • Espera de red
  • Espera de respuesta de la base de datos
  • Contención de bloqueos
  • Pausas del recolector de basura (GC)
  • Bloqueo del subproceso de la interfaz de usuario
  • Espera del lado de la GPU o del controlador
  • Escaneo de archivos por parte del antivirus
  • Frecuencia de CPU reducida hacia el lado de ahorro de energía

Además, HIGH_PRIORITY_CLASS y REALTIME_PRIORITY_CLASS no son cosas que deban usarse a la ligera.

Un subproceso que se ejecuta durante mucho tiempo con prioridad alta cede difícilmente tiempo de CPU a otros subprocesos.
Si eso queda dentro de la propia aplicación, aún se puede tolerar, pero puede llegar a empeorar la capacidad de respuesta de todo el sistema.

La prioridad es como un medicamento.

En las situaciones adecuadas, funciona.
Pero no es algo que mejore por aumentar la dosis.

Qué considerar antes de subir la prioridad

Antes de subir la prioridad, hay algunas cosas que conviene plantearse primero.

Pregunta Qué observar
¿Ese proceso realmente está esperando a la CPU? Uso de CPU, ETW (Event Tracing for Windows, el mecanismo de trazas estándar del sistema operativo), perfilador
¿No está bloqueando el subproceso de la interfaz de usuario? Respuesta de la interfaz de usuario, asincronía, diseño de colas
¿El tramo en prioridad alta es corto? Subirla temporalmente y devolverla al terminar
¿No molesta a otras aplicaciones? Impacto en la entrada, la impresión, el navegador, el software residente
¿No lo bloquean los permisos o las políticas en el entorno del cliente? Permisos de administrador, usuario de ejecución, productos de seguridad

Lo importante en una aplicación Windows no es ejecutarse siempre con la máxima prioridad, sino priorizar el proceso necesario, en el momento necesario y solo en el alcance necesario.

3. La afinidad determina “en qué CPU puede ejecutarse”

Si la prioridad es «cuándo es más probable que se ejecute», la afinidad es «en qué CPU puede ejecutarse».

En Windows se puede especificar, tanto para procesos como para subprocesos, el conjunto de procesadores lógicos en los que pueden ejecutarse.

En la API Win32 existe SetProcessAffinityMask para el proceso y SetThreadAffinityMask para el subproceso.

Para ver la afinidad de un proceso con PowerShell, por ejemplo:

Get-Process -Id $PID | Select-Object Id, ProcessName, ProcessorAffinity

Con fines de verificación, para restringir el proceso actual de PowerShell a los primeros 4 procesadores lógicos, se puede escribir así:

$p = Get-Process -Id $PID
$p.ProcessorAffinity = [IntPtr]0xF

0xF en binario es 1111.
Es decir, significa que se permiten los procesadores lógicos 0 a 3.

Ahora bien, esto es solo un ejemplo con fines de verificación.

Restringir la afinidad puede mejorar la localidad de la caché de la CPU o reducir la fluctuación del procesamiento periódico.
Por otro lado, también confina a un espacio reducido subprocesos que Windows, de manera natural, podría haber desviado hacia una CPU libre.

En particular, hay que tener cuidado en entornos como estos.

  • Cuando conviven núcleos P y núcleos E
  • Con SMT/Hyper-Threading (Simultaneous Multi-Threading, el mecanismo que muestra un solo núcleo físico como varios procesadores lógicos), donde la correspondencia entre procesador lógico y núcleo físico no es intuitiva
  • Cuando hay NUMA (Non-Uniform Memory Access, una configuración en la que la distancia hasta la memoria vista desde la CPU no es uniforme)
  • Cuando se superan los 64 procesadores lógicos y entra en juego el Processor Group
  • Cuando el Core Parking está activo
  • Cuando el control de energía del OEM o de la BIOS es fuerte
  • Cuando se ejecuta en un entorno virtualizado

La afinidad es una configuración que impone al sistema una restricción fuerte del tipo «ejecútate solo en esta CPU».

La restricción también puede ser una herramienta de estabilización.
Pero si se aplica mal, cierra vías de escape.

Ahora también existen los CPU Sets

La máscara de afinidad clásica es una restricción bastante fuerte.

Por otro lado, Windows también dispone del mecanismo CPU Sets.
CPU Sets es una API para que la aplicación exprese sus preferencias de CPU de una forma más flexible.

Según la explicación de Microsoft, CPU Sets se posiciona como una forma de afinidad “soft” (blanda) compatible con la gestión de energía del sistema operativo.

¿Se quiere fijar la CPU de forma estricta?
¿O se quiere orientar aproximadamente el lugar de ejecución mientras se coopera con la gestión de energía y la planificación del sistema operativo?

Esta diferencia es importante.

En lugar de intentar resolverlo todo con el clásico SetProcessAffinityMask, en una aplicación Windows moderna hay que pensar también en CPU Sets y en QoS.

4. Núcleos P/E: “la CPU también tiene personalidad”

En las CPU recientes, no todos los núcleos tienen el mismo rendimiento ni el mismo consumo. El ejemplo representativo son los núcleos P y los núcleos E: en términos generales, los núcleos P están orientados al rendimiento y los núcleos E están orientados a la eficiencia.

Tipo En qué destaca
Núcleo P Baja latencia, alto rendimiento por núcleo, procesamiento pesado en primer plano
Núcleo E Ahorro de energía, procesamiento en segundo plano, receptor de procesamiento paralelo

Sin embargo, es peligroso que un desarrollador dé por sentado a la ligera algo como «las CPU 0 a 7 son núcleos P y las 8 a 15 son núcleos E». El orden de numeración de las CPU puede variar según la CPU, la BIOS, la versión de Windows, el firmware, la configuración del OEM y el entorno de virtualización.

En el lado de Windows, la información de CPU Sets incluye un concepto llamado EfficiencyClass.
Es un valor que indica la característica de eficiencia de ese CPU Set en un sistema con procesadores heterogéneos. La documentación de Microsoft explica que, cuanto más alto es este valor, ese CPU Set tiene un procesador más rápido pero con menor eficiencia energética.

Si se quiere trabajar con los núcleos P/E, no basta con mirar el número de CPU. Para hacerlo en serio, hacen falta observaciones como estas.

  • Ver EfficiencyClass con la API de CPU Sets
  • Ver la CPU de ejecución de los subprocesos con Windows Performance Recorder / Analyzer (WPR / WPA, la herramienta estándar para capturar y analizar trazas de ETW)
  • Ver la tendencia en la visualización de procesadores lógicos del Administrador de tareas
  • Medir el tiempo de proceso en cada equipo físico
  • Comparar entre alimentación de CA y batería
  • Comparar cambiando el modo de energía

Los núcleos P/E no son una simple especificación de hardware: el lugar de ejecución real se determina combinándolos con el planificador de Windows, el QoS y la configuración de ahorro de energía.

5. La configuración de ahorro de energía influye en “cuánto se exige realmente a la CPU”

Aquí es donde, en el terreno, la cosa se pone bastante seria.

La prioridad es «qué subproceso se ejecuta primero».
La afinidad es «en qué CPU puede ejecutarse».
Los núcleos P/E son «si esa CPU está orientada al rendimiento o al ahorro de energía».

Y la configuración de ahorro de energía influye en

con qué frecuencia y estado de potencia se hace funcionar la CPU en primer lugar

Es decir, puede pasar lo siguiente.

Se subió la prioridad.
Se favorecieron los núcleos P.
Pero si la configuración de energía está orientada al ahorro,
puede que la CPU no se exija a fondo.

Esto es bastante característico de Windows.

En Windows 11, el modo de energía se puede elegir desde la aplicación Configuración, en «Sistema > Energía y batería».
La redacción varía según el entorno y la versión, pero la orientación general es esta.

Modo de energía Orientación
Máxima eficiencia energética / Best power efficiency Prioriza la batería y el ahorro de energía
Equilibrado / Balanced Equilibrio entre rendimiento y energía
Máximo rendimiento / Best performance Prioriza el rendimiento

Además, en los planes de energía tradicionales existen Power Saver (Economizador), Balanced (Equilibrado) y High Performance (Alto rendimiento).
Balanced ajusta el rendimiento y el consumo según la demanda, y High Performance se orienta a facilitar el máximo rendimiento a cambio de mayor consumo.

Aunque la configuración visible para el usuario es simple, detrás hay una configuración de Processor Power Management (PPM).

6. P-state, C-state, boost y EPP

La CPU no siempre funciona a la frecuencia máxima: tiene estados diseñados para reducir el consumo.

Término En pocas palabras
P-state Estado de rendimiento que cambia la frecuencia y el voltaje de la CPU
C-state Estado de ahorro de energía que detiene parte de las funciones de la CPU en reposo
Boost Mecanismo que entra en un estado de rendimiento superior al nominal cuando se cumplen ciertas condiciones
EPP Energy Performance Preference: la preferencia entre orientarse al rendimiento o al ahorro de energía

El P-state es el mecanismo que reduce el consumo cambiando la frecuencia y el voltaje de la CPU.
El C-state es el mecanismo por el cual la CPU, en reposo, detiene parte de sus funciones para entrar en un estado de ahorro de energía más profundo.

La gestión de energía de Windows usa estos mecanismos para equilibrar el rendimiento y el consumo.

Por eso puede darse el fenómeno de «prioridad alta pero lento».

El subproceso se está ejecutando con prioridad.
Pero la frecuencia de la CPU es baja.
Cuesta entrar en boost.
El EPP está orientado al ahorro de energía.
El Core Parking limita los núcleos disponibles.
Con alimentación por batería, todo el sistema operativo funciona de forma más orientada al ahorro de energía.

En estos casos, mirar solo la prioridad no lleva hasta la causa.

En particular, en el procesamiento periódico, el procesamiento de imágenes, el control de instrumentos de medición, el procesamiento de vídeo, el procesamiento de audio, la captura de cámaras USB o la comunicación serie, el problema no es solo el tiempo de proceso medio, sino que «a veces se retrasa».

En promedio es rápido.
Pero una de cada mil veces se retrasa.
Esa única vez satura el búfer.
La interfaz de usuario se congela.
Se desincroniza el tiempo con el dispositivo.

En este tipo de fenómenos, el promedio del uso de CPU no es suficiente.

Hay que observar juntos la distribución de los tiempos de proceso, el valor máximo, los valores atípicos, el estado de energía, la CPU de ejecución y la carga en segundo plano.

7. Core Parking influye en “el número de núcleos disponibles”

Windows dispone de un mecanismo llamado Core Parking. Es un control orientado a dejar en reposo los procesadores lógicos que no se están usando.
Cuando el uso es bajo, orienta parte de los núcleos hacia un estado de bajo consumo para reducir el gasto energético.

En la documentación de Microsoft, CPMinCores se describe como una configuración que especifica qué porcentaje mínimo de procesadores lógicos debe mantenerse un-parked, es decir, en estado disponible, en un momento dado.
Si el valor se pone en 100%, el algoritmo de Core Parking queda deshabilitado.

Aquí surge un problema al combinarlo con la afinidad.

Se restringió con afinidad diciendo «que se ejecute en este grupo de CPU».
Pero cómo se trata esa zona de núcleos a nivel de gestión de energía es otro asunto.

La documentación orientada a Windows Server también explica que, cuando los subprocesos activos tienen una afinidad fuertemente fijada a una parte de las CPU dentro de un nodo NUMA, hay situaciones en las que esto no encaja con las decisiones de Core Parking.

Esto también sirve de referencia conceptual en un PC cliente.

La afinidad impone restricciones al planificador.
El Core Parking, por su parte, tiene que ver con qué núcleos deja disponibles la gestión de energía.

Si se piensan estos dos aspectos por separado, se puede errar la causa.

8. EcoQoS / Efficiency Mode: el mecanismo para indicar “este proceso puede ser de bajo consumo”

Antiguamente, el ajuste de rendimiento de una aplicación Windows giraba sobre todo en torno a subir o bajar la prioridad, o cambiar la afinidad. Sin embargo, en el Windows actual existe un enfoque algo distinto: el QoS.

QoS, Quality of Service, es un concepto que indica a un subproceso «cuánto se orienta este proceso al rendimiento y cuánto al ahorro de energía».

La documentación de Microsoft explica que la prioridad de planificación sigue siendo el indicador principal para decidir qué subproceso se ejecuta a continuación, mientras que el QoS puede influir en la selección de núcleos y en la gestión de energía del procesador.

En particular, EcoQoS es un mecanismo para tratar de forma orientada al ahorro de energía los procesos en los que el rendimiento no es lo más importante.

En C++, por ejemplo, se puede poner el subproceso actual en EcoQoS usando SetThreadInformation y ThreadPowerThrottling.

#include <windows.h>

void EnableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

A la inversa, para volver a priorizar el rendimiento, se pone StateMask en 0 para el mismo objeto de control.

void DisableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = 0;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

Si se usa desde C#, la biblioteca de clases de .NET no tiene un tipo dedicado para configurar EcoQoS. Se llama a SetThreadInformation mediante P/Invoke. A continuación, una forma de escribirlo pensada para .NET 8.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

internal static class EcoQos
{
    // ThreadPowerThrottling de THREAD_INFORMATION_CLASS
    private const int ThreadPowerThrottling = 3;

    // THREAD_POWER_THROTTLING_CURRENT_VERSION
    private const uint CurrentVersion = 1;

    // THREAD_POWER_THROTTLING_EXECUTION_SPEED
    private const uint ExecutionSpeed = 0x1;

    [StructLayout(LayoutKind.Sequential)]
    private struct ThreadPowerThrottlingState
    {
        public uint Version;
        public uint ControlMask;
        public uint StateMask;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetCurrentThread();

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetThreadInformation(
        IntPtr hThread,
        int threadInformationClass,
        ref ThreadPowerThrottlingState threadInformation,
        uint threadInformationSize);

    /// <summary>Pone el subproceso actual en EcoQoS (false para volver a priorizar el rendimiento).</summary>
    public static void SetForCurrentThread(bool enabled)
    {
        var state = new ThreadPowerThrottlingState
        {
            Version = CurrentVersion,
            ControlMask = ExecutionSpeed,
            StateMask = enabled ? ExecutionSpeed : 0u,
        };

        var ok = SetThreadInformation(
            GetCurrentThread(),
            ThreadPowerThrottling,
            ref state,
            (uint)Marshal.SizeOf<ThreadPowerThrottlingState>());

        if (!ok)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}

Del lado de quien lo invoca, quedaría así. EcoQoS es una configuración por subproceso, así que lo más seguro es levantar un subproceso dedicado y activarla y desactivarla dentro de él.

using System.Threading;

// RunBackgroundMaintenance es un "proceso sin urgencia" que se prepara uno mismo
var worker = new Thread(() =>
{
    EcoQos.SetForCurrentThread(true);
    try
    {
        RunBackgroundMaintenance();
    }
    finally
    {
        // Al terminar, siempre hay que devolverlo
        EcoQos.SetForCurrentThread(false);
    }
})
{
    IsBackground = true,
};

worker.Start();

Aquí conviene tener cuidado con no aplicarlo directamente a Task.Run ni a los subprocesos del grupo de subprocesos (thread pool). El objeto sobre el que se aplica es el subproceso del sistema operativo, no el Task que se está ejecutando. Si se pone EcoQoS en un subproceso del pool, ese subproceso seguirá orientado al ahorro de energía la próxima vez que se le asigne otro trabajo distinto. Si con async/await la continuación pasa a otro subproceso, la configuración puede quedar activa o no fuera del tramo previsto.

EcoQoS no es una función que consista en «poner todo en modo ahorro de energía». Es adecuada, por ejemplo, para procesos como estos.

  • Sincronización en segundo plano
  • Creación de índices de baja prioridad
  • Recopilación de registros sin urgencia
  • Actualización de caché sin relación directa con la interacción del usuario
  • Tareas de mantenimiento que basta con terminar más adelante

Por otro lado, hay que ser cauteloso con procesos como estos.

  • Procesos ligados directamente a operaciones de la interfaz de usuario
  • Captura de cámara
  • Procesamiento de audio
  • Procesos relacionados con ciclos de control
  • Procesos de evaluación de equipos de inspección
  • Procesos de exportación que el usuario está esperando
  • Procesos que necesitan una respuesta cercana al tiempo real

El Efficiency mode del Administrador de tareas también está relacionado con esta idea.
El blog de Performance Diagnostics de Microsoft explica que, al activar Efficiency mode, se reduce la prioridad base del proceso a Low y se configura el QoS como EcoQoS.

Es decir, Efficiency mode no es un simple «icono de ahorro de energía»: es un mecanismo que combina la reducción de prioridad y EcoQoS para proteger la capacidad de respuesta y la eficiencia energética de la aplicación en primer plano.

Desde el punto de vista del desarrollador, esto es bastante importante.

Porque permite diseñar no solo pensando en «quiero que este proceso termine rápido», sino también transmitiendo al sistema operativo que

«este proceso puede ejecutarse en la medida en que no moleste al usuario»

9. Diagrama de flujo de decisión

Todo lo anterior es bastante complejo, así que aquí dejamos un flujo de decisión para cuando se investiga el rendimiento o la fluctuación de ciclo de una aplicación Windows.

Diagrama de flujo para diagnosticar problemas de rendimiento y fluctuación en aplicaciones WindowsDiagrama de flujo que parte de un síntoma de lentitud o fluctuación, pasa por el registro de datos, y va descartando causas ajenas a la CPU, la contención con otros procesos, el sesgo hacia una CPU concreta, el estado de frecuencia y energía, y la interferencia de procesos en segundo plano, hasta llegar a una medición A/B por cambios individualesNo / DesconocidoNoNoNoNoSíntomaLentitud, fluctuación del ciclo, la interfaz se congela, el ventilador hace ruidoPrimero, registrarTiempo de proceso / valor máximo / uso de CPU / estado de energía / CA o batería / PC objetivo¿La CPU es la causa principal?Revisar E/S, bloqueos, BD, red, GC, GPU, controladoresReducir los tiempos de espera antes de tocar la prioridad o la afinidad¿Hay fuerte contención de CPU con otros procesos?Considerar la prioridadPero limitando el alcanceEvitar el uso habitual de High o Realtime¿Está sesgado hacia una CPU específica?Revisar afinidad / CPU Sets¿Se está fijando demasiado?¿Se están considerando los núcleos P/E y NUMA?¿La frecuencia o el estado de energía son sospechosos?Revisar el modo de energía / plan de energía / PPMSospechar de P-state, EPP, boost, Core Parking¿El procesamiento en segundo plano interfiere con el primer plano?Considerar EcoQoS / Efficiency Mode / bajar la prioridadDesviar los procesos sin urgencia hacia el lado de ahorro de energíaRevisar el algoritmo, el grado de paralelismo, el diseño de colas y el diseño del subproceso de interfaz de usuarioCambiar un elemento a la vez y medir con pruebas A/BNo solo el promedioObservar también el valor máximo, los valores atípicos, el calentamiento y el impacto en otras aplicaciones

Figura 2: diagrama de flujo para diagnosticar problemas de rendimiento y fluctuación de ciclo en aplicaciones Windows.

Lo importante de este flujo es no tocar la configuración desde el principio. Primero se registra.

  • ¿La lentitud es constante?
  • ¿O es ocasional?
  • ¿Con alimentación de CA o con batería?
  • ¿Cuál es el modo de energía?
  • ¿No está en Efficiency mode en el Administrador de tareas?
  • ¿El uso de CPU es alto?
  • ¿La frecuencia está subiendo?
  • ¿Qué subproceso está usando la CPU?
  • ¿Cuál es el valor máximo del tiempo de proceso?
  • ¿No están involucrados otros programas residentes o el antivirus?

A partir de ahí, se cambia una cosa a la vez.

Cambiar la prioridad.
Cambiar la afinidad.
Cambiar el modo de energía.
Añadir EcoQoS.
Separar el procesamiento en segundo plano.
Añadir una cola.
Sacarlo del subproceso de la interfaz de usuario.

Si se cambian varias cosas a la vez, deja de saberse qué fue lo que surtió efecto.

10. Comandos de verificación en el terreno

Cuando el comportamiento de una aplicación Windows varía según el entorno, conviene primero poder capturar el estado del sistema.

Ver la información de la CPU

Get-CimInstance Win32_Processor |
  Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed

Ver el plan de energía activo

powercfg /getactivescheme

Ver la configuración de gestión de energía del procesador

powercfg /q SCHEME_CURRENT SUB_PROCESSOR

La salida es bastante extensa, pero permite comprobar la configuración relacionada con el estado mínimo y máximo del procesador, el EPP, el boost y el Core Parking.
Los elementos visibles varían según el entorno.

Generar el informe de diagnóstico de eficiencia energética

Se ejecuta en una terminal con privilegios de administrador.

powercfg /energy

Tras un periodo de medición, se genera un informe en HTML.
Es un buen punto de partida para revisar controladores, dispositivos, resolución del temporizador, ahorro de energía de USB e impedimentos para la suspensión.

Ver la prioridad y la afinidad del proceso objetivo

Get-Process -Name MyApp |
  Select-Object Id, ProcessName, PriorityClass, ProcessorAffinity

Verificar con el Administrador de tareas

En el equipo del cliente, a veces no se puede abrir PowerShell. Cuando se quiere verificar solo con la interfaz gráfica, hay que mirar en estos lugares. La redacción varía según la versión de Windows, así que también se indica el nombre en inglés entre paréntesis.

Qué se quiere ver Operación
Prioridad del proceso Administrador de tareas > pestaña «Detalles» (Details) > clic derecho en el encabezado de columna > «Seleccionar columnas» > marcar «Prioridad base» (Base priority)
Cambiar la prioridad En la pestaña «Detalles», clic derecho sobre el proceso > «Establecer prioridad» (Set priority)
Ver o cambiar la afinidad En la pestaña «Detalles», clic derecho sobre el proceso > «Establecer afinidad» (Set affinity)
Estado del modo de eficiencia En la columna «Estado» (Status) de la pestaña «Procesos» (Processes) aparece «Modo de eficiencia». Los procesos principales que ponen en modo de eficiencia a sus procesos secundarios muestran un icono de hoja
Activar el modo de eficiencia En la pestaña «Procesos», seleccionar el proceso y pulsar «Modo de eficiencia» en la barra de comandos, o elegirlo desde el menú contextual
Carga por cada procesador lógico Pestaña «Rendimiento» > «CPU» > clic derecho en el gráfico > «Cambiar gráfico a» > «Procesadores lógicos»

El modo de eficiencia no se puede aplicar a los procesos centrales de Windows, porque detenerlos afectaría a la estabilidad del sistema.

Además, el modo de eficiencia no es una simple etiqueta visual. El blog de Performance Diagnostics de Microsoft explica que el modo de eficiencia reduce la prioridad base del proceso a Low y configura el QoS como EcoQoS. Es decir, desde la interfaz gráfica solo se están aplicando de una vez las dos configuraciones descritas en el capítulo 8.

Cabe señalar que lo que se ve en la pestaña «Detalles» del Administrador de tareas es, en todo caso, la prioridad a nivel de proceso. No se muestra la prioridad relativa por subproceso ni el QoS de cada subproceso. Para llegar a ese nivel, hay que capturar ETW con WPR/WPA.

Ver el estado de rendimiento de la CPU

Los nombres de contadores disponibles varían según el entorno, pero estos Performance Counter son un buen punto de referencia.

Get-Counter '\Processor Information(_Total)\% Processor Performance'
Get-Counter '\Processor Information(_Total)\% Processor Utility'

Para verlo en profundidad, lo más fiable es capturar ETW con Windows Performance Recorder (WPR) y Windows Performance Analyzer (WPA).
Así se puede seguir de forma conjunta la fluctuación de ciclo, los cambios de contexto, la CPU de ejecución, las DPC/ISR (Deferred Procedure Call / Interrupt Service Routine: el procesamiento de interrupciones de los controladores y su ejecución diferida), la E/S de disco y los cambios de frecuencia de la CPU.

11. En el procesamiento de tiempo real blando, hay que considerarlo todo junto

Windows no es, en el sentido general del término, un sistema operativo de tiempo real. Sin embargo, en las aplicaciones Windows del terreno a veces se exigen propiedades cercanas al tiempo real.

  • Capturar imágenes a intervalos regulares desde una cámara USB
  • Comunicarse con un equipo mediante comunicación serie
  • Leer datos de un instrumento de medición
  • Sincronizarse con un PLC u otro dispositivo externo
  • Procesar audio o vídeo
  • Devolver un resultado de inspección dentro de un tiempo determinado
  • Ejecutar cálculos pesados sin congelar la interfaz de usuario

En este tipo de procesos, la velocidad del código por sí sola no basta.

Hace falta un diseño de subprocesos.
Hace falta un diseño de colas.
Hacen falta registros (logs).
Hacen falta tiempos de espera límite (timeouts).
Hace falta una contrapresión, backpressure (el mecanismo que reduce deliberadamente el flujo de entrada cuando el destino no puede procesarlo todo).
Hay que decidir si, cuando algo se retrasa, se descarta, se espera o se reintenta.

Y, sobre esa base, hay que examinar la prioridad, la afinidad, los núcleos P/E y la configuración de ahorro de energía.

Pensemos, por ejemplo, en el subproceso de captura de una cámara.

Este subproceso está cerca de la interacción del usuario y también tiene periodicidad.
Por eso no se le debería aplicar EcoQoS.
Vale la pena subir la prioridad un nivel, pero solo durante el tramo de captura. Aun así, no se debe usar de forma habitual HIGH_PRIORITY_CLASS o superior: perjudicaría a la interfaz de usuario y a otros procesos.
La afinidad solo debe fijarse cuando la medición real muestra correlación entre la fluctuación de ciclo y el movimiento entre CPU. Y si se fija, hay que elegir el lado de los núcleos P observando EfficiencyClass, no el número de CPU. Fijarla en el lado de los núcleos E resulta contraproducente.
Si el modo de energía está orientado al ahorro, aparecerán con batería retrasos que no se daban con alimentación de CA. Por eso las pruebas de rendimiento deben incluir siempre la condición de batería.

Por otro lado, ¿qué pasa con un proceso de compresión de registros antiguos?

Este es un ejemplo representativo de proceso que el usuario no está esperando.
En ese caso, conviene bajar la prioridad.
Ponerlo en EcoQoS.
Ejecutarlo en momentos de inactividad.
Ejecutarlo solo con alimentación de CA.
Este tipo de diseño resulta más amable para la aplicación en su conjunto.

No se trata de hacer «rápido» todo, sino de separar los procesos que deben acelerarse de los que no deben molestar.

12. Lineamientos básicos de diseño

A la hora de tratar estos aspectos en una aplicación Windows, hay seis lineamientos básicos.

1. No fijar nada desde el principio

Es mejor no fijar con fuerza la prioridad ni la afinidad desde el principio.

El planificador de Windows, en muchos casos, hace un trabajo bastante bueno.
Que la aplicación imponga restricciones innecesarias puede, al contrario, empeorar las cosas.

Primero se construye de forma normal.
Se mide.
Si aparece un problema, se plantea una hipótesis.
Se cambia algo pequeño.
Se vuelve a medir.

Ese es el orden.

2. Usar la prioridad de forma temporal

La prioridad alta se reserva solo para el tramo necesario.

En lugar de mantenerla siempre alta, es más seguro

subirla justo antes del proceso importante
y devolverla al terminar

3. Evaluar la afinidad al final

La afinidad es una configuración fuerte.

Puede ser eficaz cuando se sospecha del impacto de la fluctuación de ciclo o del movimiento entre CPU.
Pero no es la configuración que se toca primero.

En particular, si existen varios modelos de PC en el parque de clientes, dar por sentado un número de CPU fijo es peligroso.

4. Los núcleos P/E se “observan”, no se “asumen”

Si se quiere diferenciar entre núcleos P y núcleos E, primero hay que observarlo en el equipo real.

En lugar de asumirlo por el número de CPU, hay que verlo con CPU Sets, EfficiencyClass, ETW y mediciones reales.

5. Probar partiendo de la configuración de ahorro de energía

No basta con que la máquina de desarrollo, con alimentación de CA y configuración de alto rendimiento, sea rápida.

En el entorno del cliente, es habitual encontrarse con estados como estos.

  • Portátil funcionando con batería
  • Best power efficiency
  • Energy saver
  • Utilidad de ahorro de energía propia del OEM
  • Configuración de energía fijada por política corporativa
  • PC delgado cuyo rendimiento baja por el calor
  • Equipo de trabajo con muchos programas residentes

En las pruebas de rendimiento conviene distinguir al menos estas condiciones.

Condición Qué observar
CA + Best performance Estado cercano al rendimiento máximo
CA + Balanced Uso empresarial estándar
Batería + Balanced La realidad de un portátil
Batería + Best power efficiency El límite inferior orientado al ahorro de energía
Ejecución continua prolongada Calor, ventilador, thermal throttling
Uso simultáneo de otras aplicaciones Convivencia con navegador, Teams, Excel, antivirus

6. Considerar EcoQoS para el procesamiento en segundo plano

Para los procesos que no afectan directamente a la experiencia del usuario, en ocasiones conviene priorizar no molestar antes que perseguir el rendimiento.

Una aplicación que ejecuta todo con alto rendimiento puede ser rápida.
Pero interfiere con otras tareas.
Hace girar el ventilador del portátil.
Consume batería.
Como resultado, se vuelve una aplicación incómoda de usar.

En una aplicación Windows, la «habilidad para convivir» también es calidad, no solo la velocidad.

13. Malentendidos habituales

Subir la prioridad siempre acelera la aplicación

No necesariamente se acelera.

Si la causa es la contención de CPU, puede surtir efecto.
Pero si la causa es la espera de E/S, la espera de bloqueos, una frecuencia baja por ahorro de energía o la ejecución en el lado de los núcleos E, la prioridad por sí sola no basta.

Fijar todo en los núcleos P siempre es la respuesta correcta

No siempre es la respuesta correcta.

Los núcleos P tienen alto rendimiento, pero también aumentan el calentamiento y el consumo.
Además, pueden entrar en competencia con otros procesos importantes en primer plano.

Es más importante separar los procesos que deben favorecer los núcleos P de los que se conforman con un núcleo E.

La configuración de ahorro de energía es cosa del usuario y no afecta a la aplicación

Sí que afecta.

Con la misma aplicación, la forma de ejecución cambia según el modo de energía, el plan de energía, el EPP, el boost y el Core Parking.

En particular, en los portátiles el comportamiento cambia entre alimentación de CA y batería.

Efficiency mode solo ralentiza las cosas

No se limita a ralentizar.

Es un mecanismo que, mediante la reducción de prioridad y EcoQoS, protege la capacidad de respuesta y la eficiencia energética de la aplicación en primer plano.
Para el procesamiento en segundo plano sin urgencia, vale la pena considerarlo de forma activa.

Windows es inestable por naturaleza

Esta también es una visión superficial.

Windows no es un sistema operativo de tiempo real.
Pero si se comprenden la planificación, la gestión de energía, el QoS, la medición, el registro y el diseño de subprocesos, se puede lograr una estabilidad bastante adecuada para el terreno.

El problema no es «con Windows es imposible», sino no observar qué está ocurriendo en cada capa.

14. Diseñe el registro (logging) antes que la implementación

Este tipo de problema a veces solo se manifiesta en el entorno del cliente. Por eso ayuda que la propia aplicación pueda dejar como mínimo información de diagnóstico.

Por ejemplo, en el registro de diagnóstico se puede volcar información como esta.

  • Versión de la aplicación
  • Versión de Windows
  • Nombre de la CPU
  • Número de procesadores lógicos
  • Alimentación de CA o batería
  • Promedio, máximo y percentiles del tiempo de proceso
  • Número de retrasos del ciclo de procesamiento
  • Prioridad del subproceso objetivo
  • Prioridad del proceso
  • Si hay configuración de afinidad
  • Si hay configuración de EcoQoS
  • Plan de energía al iniciar
  • Número de veces que el proceso objetivo agotó el tiempo de espera

Cuando el cliente dice que «a veces va lento», si no hay ningún registro, todo se convierte en una apuesta a base de conjeturas. Con registros, se pueden plantear hipótesis.

Solo va lento con batería
Solo va lento en un PC específico
Solo va lento justo después de arrancar
Va lento a partir de los 30 minutos
Solo va lento mientras hay otra aplicación abierta
Aparecen valores atípicos a intervalos regulares

Si se pueden ver este tipo de diferencias, resulta más fácil discernir si se trata de la prioridad, la afinidad, la configuración de energía, el calor o alguna otra espera.

15. La perspectiva de KomuraSoft

En el desarrollo de aplicaciones Windows, la teoría pura no basta.

Que funcione en el PC del cliente.
Que funcione en el equipo del terreno.
Que conviva con periféricos antiguos.
Que funcione en entornos con antivirus, impresoras y políticas internas.
Que no se rompa con la configuración de ahorro de energía de un portátil.
Que, donde se necesita rendimiento, lo entregue de verdad.
Que, en los procesos sin urgencia, no moleste al usuario.

Para lograrlo, conviene no ver Windows como una simple «caja negra de sistema operativo».

Windows está pensando en cómo mover los subprocesos.
Está pensando en qué CPU ejecutarlos.
Está equilibrando energía y rendimiento.
Está gestionando núcleos heterogéneos como los P/E.
Está intentando distinguir entre la aplicación en primer plano y el procesamiento en segundo plano.

El desarrollador, en lugar de ir en contra de eso, debería comunicar su intención donde haga falta.

Este proceso tiene al usuario esperando.
Este proceso puede ir algo lento sin problema.
En este proceso importa el ciclo.
Este proceso puede ejecutarse en silencio en segundo plano.
Este proceso no debe interferir con otros procesos.

Ese diseño se traduce en la comprensión de la prioridad, la afinidad, el QoS y la configuración de ahorro de energía.

Qué leer a continuación

En este artículo se trató la configuración del lado de la CPU. Cuando la causa de la lentitud está fuera de la CPU, el siguiente artículo suele ser el camino más directo.

Síntoma Siguiente artículo a leer
No se sabe dónde se está consumiendo el tiempo PerfView y dotnet-trace para identificar la lentitud — introducción práctica al análisis de rendimiento en .NET
Se quiere construir desde cero un mecanismo para registrar el entorno del cliente Introducción al registro de eventos de Windows y ETW — cómo llevar los registros de una aplicación empresarial al mecanismo estándar del sistema operativo
Se sospecha de la espera de E/S o de saturación del grupo de subprocesos Las profundidades de la E/S de Windows (tercera entrega) — el puerto de finalización de E/S (IOCP) y el grupo de subprocesos de .NET
La memoria sigue creciendo y todo se vuelve lento Cómo distinguir entre una espera del GC y una fuga de memoria en .NET
Solo se congela la interfaz de usuario async y el subproceso de interfaz de usuario en WPF/WinForms, resumidos en una sola hoja

16. Resumen

El rendimiento de una aplicación Windows no depende solo del código.

Aunque se suba la prioridad, si la afinidad favorece el lado de los núcleos E, no funcionará como se espera.
Aunque se favorezcan los núcleos P, si el modo de energía está orientado al ahorro, la CPU no dará su máximo rendimiento.
Si se aplica una afinidad fuerte sin pensar en CPU Sets ni en QoS, se cierran las vías de escape de la gestión de energía y del planificador de Windows.
Si todo se orienta al lado de alto rendimiento, aumentan el calentamiento, el ruido del ventilador, el consumo de batería y el impacto en otras aplicaciones.

En el desarrollo de aplicaciones Windows hay que diseñar teniendo en cuenta no solo la velocidad, sino también la capacidad de respuesta, la estabilidad, el consumo, el calentamiento y la convivencia con otros procesos.

Para ello, lo que hay que observar son, de nuevo, estas cuatro cosas.

Prioridad
Afinidad
Núcleos P / Núcleos E
Configuración de ahorro de energía

Y en el Windows actual, a eso se suman EcoQoS y Efficiency mode.

Hay muchas cosas de las que preocuparse.

Pero no hace falta dejar esa preocupación convertida en simple inquietud.

Convertir la preocupación en medición.
Convertir la preocupación en registros.
Convertir la preocupación en diseño.
Convertir la preocupación en condiciones de prueba.

Así, Windows deja de ser un entorno de ejecución caprichoso para convertirse en una plataforma bastante interesante de observar y, además, fiable para el terreno.

Windows no es un sistema operativo de tiempo real.
Aun así, si se comprende y se diseña el entorno de ejecución, es posible crear aplicaciones lo bastante confiables para el terreno.

Y esa misma dificultad es, precisamente, parte del atractivo del desarrollo de aplicaciones Windows.

Referencias

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Cómo se configura la prioridad de CPU?
La prioridad se determina en dos niveles: la clase de prioridad del proceso y la prioridad relativa del subproceso (thread). En la API Win32 existe SetPriorityClass para el proceso y SetThreadPriority para el subproceso. En PowerShell se puede asignar un valor como "AboveNormal" a la propiedad PriorityClass del objeto obtenido con Get-Process, y en C# se asigna ProcessPriorityClass.AboveNormal a la propiedad PriorityClass de Process.GetCurrentProcess(). Sin embargo, debe evitarse el uso habitual de HIGH_PRIORITY_CLASS o REALTIME_PRIORITY_CLASS, ya que empeoran la capacidad de respuesta de todo el sistema.
¿Por qué la aplicación no se vuelve más rápida aunque se suba la prioridad?
Porque la prioridad es una configuración que determina «qué subproceso se ejecuta primero cuando hay contención», no una configuración que hace que la CPU sea más rápida. Si el origen de la lentitud es la espera de E/S de disco, la espera de red, la contención de bloqueos, las pausas del recolector de basura (GC), el bloqueo del subproceso de la interfaz de usuario, el escaneo de un antivirus, o una frecuencia de CPU reducida por la configuración de ahorro de energía, subir la prioridad no resuelve el problema de fondo. Lo importante es registrar primero la distribución de los tiempos de proceso, el estado de energía y la CPU de ejecución para aislar la causa.
¿Qué ocurre al configurar EcoQoS con SetThreadInformation?
Al especificar ThreadPowerThrottling y THREAD_POWER_THROTTLING_EXECUTION_SPEED en SetThreadInformation, se le indica al sistema operativo que trate ese subproceso como EcoQoS (un QoS orientado al ahorro de energía). Como el QoS puede influir en la selección de núcleos y en la gestión de energía del procesador, procesos como la sincronización en segundo plano o la recopilación de registros sin urgencia pueden desviarse hacia el lado de ahorro de energía sin molestar a la aplicación en primer plano. Por otro lado, hay que ser cauteloso con procesos ligados directamente a operaciones de interfaz de usuario, la captura de cámara o procesos relacionados con ciclos de control. Poniendo StateMask en 0 se puede volver a priorizar el rendimiento.
¿Qué es CPMinCores de powercfg?
CPMinCores es una configuración de energía de Windows relacionada con el Core Parking que especifica qué porcentaje mínimo de procesadores lógicos debe mantenerse un-parked (disponibles) en un momento dado. Si el valor se establece en 100%, el algoritmo de Core Parking queda deshabilitado. La configuración actual se puede consultar con privilegios de administrador ejecutando powercfg /q SCHEME_CURRENT SUB_PROCESSOR, y cuando la CPU está restringida mediante afinidad, hay situaciones en las que esto no encaja con las decisiones de Core Parking, por lo que ambos aspectos deben examinarse juntos.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog