Me dijeron que cuando la escritura de Microsoft específicos de código de C++ escrito Sleep(1) es mucho mejor que Sleep(0) para spinlocking, debido al hecho de que Sleep(0) va a utilizar más el tiempo de CPU, por otra parte, sólo los rendimientos de si hay otro igual prioridad subproceso en espera de ejecución.

Sin embargo, con el C++11 hilo de la biblioteca, no hay mucha documentación (al menos que yo he sido capaz de encontrar) acerca de los efectos de std::this_thread::yield() vs std::this_thread::sleep_for( std::chrono::milliseconds(1) ); el segundo es, sin duda, más detallado, pero son igual de eficiente para un spinlock, o padece de potencialmente los mismos errores que afectaron a Sleep(0) vs Sleep(1)?

Un ejemplo de bucle donde std::this_thread::yield() o std::this_thread::sleep_for( std::chrono::milliseconds(1) ) sería aceptable:

void SpinLock( const bool& bSomeCondition )
{
    //Wait for some condition to be satisfied
    while( !bSomeCondition )
    {
         /*Either std::this_thread::yield() or 
           std::this_thread::sleep_for( std::chrono::milliseconds(1) ) 
           is acceptable here.*/
    }

    //Do something!
}
  • posibles duplicados de std::this_thread::yield() vs std::this_thread::sleep_for()?
  • Yo no creo que esto sea un duplicado, ya que abarca un caso específico, las respuestas a esa pregunta no directa/indirectamente contestar a mi pregunta aquí.
  • Es por eso que existe un «posible» en frente de ella. 😉 Lo que están pidiendo es totalmente implementación específica y no relacionados con el estándar de C++, el estándar de la parte debe ser cubierto en las preguntas relacionadas. yield vs sleep_for(1ms) son totalmente diferentes semántica, en el primer caso estás diciendo que la implementación de la producción, que podría ceder y después de regresar de una cantidad aleatoria de tiempo, mientras que en el segundo caso, dice que no quiere dormir más corto que el de 1ms.
  • Yo creo que el término spinlock es engañosa en su ejemplo. A mi entender, un spinlock se utiliza para evitar los cambios de contexto, por lo que se utiliza ni el sueño, ni el rendimiento. Si usted no quiere perder ciclos de la CPU, y se puede vivir con posibles cambios de contexto, sólo tiene que utilizar std::mutex o std::condition_variable, porque la mayoría de la probabilidad de que estas operaciones ya están optimizados en su plataforma.
  • Ambos son igualmente ineficaces.
  • No sólo es ineficiente, sino que además no válido desde bSomeCondition no es un tipo atómico.
  • Ellos no son tanto ineficaz para su uso en un spinlock, que estás loco para su uso en un spinlock.

4 Comentarios

  1. 28

    La Norma es algo difusa aquí, como una forma concreta de aplicación en gran parte influenciado por las capacidades de programación del sistema operativo subyacente.

    Que se dice, se puede asumir con seguridad un par de cosas en cualquier sistema operativo moderno:

    • yield dará la actual timeslice y vuelva a insertar el hilo en la programación de la cola. La cantidad de tiempo que expira hasta que el hilo se ejecuta de nuevo es generalmente todo depende del programador. Tenga en cuenta que la Norma habla de rendimiento como un oportunidad para reprogramar. Así que una aplicación es completamente libre de regresar de un rendimiento inmediato, si lo desea. Un rendimiento nunca va a marcar un hilo como inactivo, por lo que un hilo girando en un rendimiento siempre va a producir un 100% de la carga en un núcleo. Si no hay otros hilos están listos, que son propensos a perder la mayoría en el resto de la actual timeslice antes de llegar programado de nuevo.
    • sleep_* bloquea el subproceso de, al menos, la cantidad solicitada de tiempo. Una aplicación puede convertir a una sleep_for(0) en un yield. El sleep_for(1) por otro lado le enviaremos a su hilo en suspensión. En lugar de volver a la programación de la cola, el hilo va a una cola diferente a la de dormir hilos de primera. Sólo después de que la cantidad solicitada de tiempo que ha pasado el programador considere la posibilidad de volver a insertar el hilo en la programación de la cola. La carga producida por un pequeño sueño todavía será muy alta. Si el tiempo de sueño es más pequeño que un sistema de timeslice, usted puede esperar que el hilo solo de saltar un timeslice (es decir, un rendimiento para la liberación de los activos de timeslice y, a continuación, saltar el uno después), que le conducen a una carga de la cpu de cerca o incluso igual a 100% en un núcleo.

    Un par de palabras acerca de qué es mejor para el spin-bloqueo. Spin-el bloqueo es una herramienta de elección cuando se esperaba poca o ninguna contención en la cerradura. Si en la gran mayoría de los casos se espera que el bloqueo esté disponible, spin-locks son baratos y valiosa solución. Sin embargo, tan pronto como usted tiene la contención, el spin-locks se costo a usted. Si usted es de preocuparse de si el rendimiento o el sueño es la mejor solución aquí spin-locks son la herramienta equivocada para el trabajo. Usted debe usar un mutex lugar.

    Para un spin-lock, en el caso de que realmente tiene que esperar a que el bloqueo debe ser considerada excepcional. Por lo tanto, está perfectamente bien sólo el rendimiento de aquí – se expresa la intención clara y perdiendo el tiempo de la CPU nunca debe ser un motivo de preocupación en el primer lugar.

    • no es acerca de contención, pero con ciclos de cpu gastado en la sección crítica. para uncontended caso CriticalSection(Win32) Futex(Linux) lleva casi igual tiempo de CPU, que es algo así como el doble de operaciones CAS. SpinLocks puede ser implementado con una sola CAS de todos modos.
  2. 14

    Acabo de hacer una prueba con Visual Studio 2013 en Windows 7, 2.8 GHz i7 de Intel, versión por defecto modo de optimizaciones.

    sleep_for(distinto de cero) aparece dormir un minimium de alrededor de una milésima de segundo, y no toma los recursos de la CPU en un bucle como:

    for (int k = 0; k < 1000; ++k)
        std::this_thread::sleep_for(std::chrono::nanoseconds(1));

    Este bucle de 1.000 duerme toma alrededor de 1 segundo si el uso de 1 nanosegundo, 1 microsegundo, o de 1 milisegundo. Por otro lado, el rendimiento() toma alrededor de 0,25 microsegundos cada uno, pero que hará girar la CPU a 100% para el hilo:

    for (int k = 0; k < 4,000,000; ++k) (commas added for clarity)
        std::this_thread::yield();

    std::this_thread::sleep_for((std::chrono::nanosegundos(0)) parece ser aproximadamente el mismo rendimiento() (prueba no se muestra aquí).

    En comparación, el bloqueo de un atomic_flag para un spinlock toma alrededor de 5 nanosegundos. Este bucle es de 1 segundo:

    std::atomic_flag f = ATOMIC_FLAG_INIT;
    for (int k = 0; k < 200,000,000; ++k)
        f.test_and_set();

    También, un mutex toma alrededor de 50 nanosegundos, 1 segundo de este bucle:

    for (int k = 0; k < 20,000,000; ++k)
        std::lock_guard<std::mutex> lock(g_mutex);

    Basado en esto, lo que probablemente no dudéis en poner un rendimiento en el spinlock, pero estoy casi seguro que no lo uso sleep_for. Si usted piensa que su cerradura girando mucho y están preocupados por el consumo de cpu, me gustaría cambiar a std::mutex si que es práctico en su aplicación. Esperemos que los días de muy mal rendimiento en std::exclusión mutua en Windows están detrás de nosotros.

    • Realmente espero que los comas no estaban en el código que realmente se ejecutó.
  3. 3

    si usted está interesado en la carga de la cpu, mientras que el uso de rendimiento es muy malo, a excepción de un caso(sólo se ejecuta la aplicación, y que son conscientes de que será, básicamente, comer todos los recursos)

    aquí es más explicación:

    • ejecutando el rendimiento en el bucle se asegurará de que la cpu de la liberación de la ejecución de hilo, todavía, si el sistema intenta volver a hilo se acaba de repetir el rendimiento de la operación. Esto puede hacer que el hilo completo en el 100% de la carga de la cpu de núcleo.
    • ejecución sleep() o sleep_for() también es un error, esto bloquea el subproceso de ejecución, pero usted va a tener algo como el tiempo de espera en la cpu. No se equivoquen, este ES el trabajo de la cpu, pero en menor prioridad posible. Mientras que de alguna manera trabajan para simples ejemplos de uso ( totalmente a la carga de la cpu en el sueño() es la mitad que el malo como plenamente la carga de trabajo del procesador ), si desea asegurarse de que la aplicación de la responsabilidad, te gustaría algo como tercer ejemplo:
    • combinar! :

      std::chrono::milliseconds duration(1);
      while (true)
         {
            if(!mutex.try_lock())
            {
                 std::this_thread::yield();
                 std::this_thread::sleep+for(duration);
                 continue;
            }
            return;
         }

    algo como esto asegurará que la cpu va a ceder tan rápido como esta operación va a ser ejecutado, y también sleep_for() se asegurará de que la cpu va a esperar algún tiempo antes de que incluso tratando de ejecutar la siguiente iteración. Este tiempo puede ser de curso dynamicaly (o staticaly) ajustado para que se adapte a sus necesidades

    saludos 🙂

  4. 1

    Lo que quieres es probablemente una variable de condición. Una variable de condición con un condicional de despertador función se implementa normalmente gusta lo que está escrito, con el sueño o el rendimiento en el interior del bucle de espera en la condición.

    El código quedaría así:

    std::unique_lock<std::mutex> lck(mtx)
    while(!bSomeCondition) {
        cv.wait(lck);
    }

    O

    std::unique_lock<std::mutex> lck(mtx)
    cv.wait(lck, [bSomeCondition](){ return !bSomeCondition; })

    Todo lo que necesitas hacer es notificar a la variable de condición en otro hilo cuando los datos están listos. Sin embargo, no puede evitar un bloqueo de allí si desea utilizar la variable de condición.

Dejar respuesta

Please enter your comment!
Please enter your name here