2011-07-19 13 views
8

Si alguien ha tenido mucha experiencia con el código de tiempo ejecutándose en el hilo principal de VCL contra un hilo de fondo, me gustaría obtener una opinión. Tengo un código que ejecuta un procesamiento de cadenas pesadas en mi aplicación Delphi 6 en el hilo principal. Cada vez que ejecuto una operación, el tiempo para cada operación oscila alrededor de 50 ms en una sola hebra en mi i5 Quad core. Lo que me hace realmente sospechoso es que el mismo código que se ejecuta en un viejo Pentium 4 que tengo, muestra el mismo tiempo para la operación cuando generalmente veo que el código se ejecuta 4 veces más lento en el Pentium 4 que en el Quad Core. Estoy comenzando a preguntarme si el código puede consumir mucho menos tiempo que 50 ms, pero hay algo sobre el hilo principal de VCL, tal vez el manejo de mensajes de Windows o la ejecución de llamadas a la API de Windows, que está creando un "piso" artificial para la operación. Tenga en cuenta que, si eso es importante, una solicitud entrante inicia una operación, pero la medición de tiempo no se realiza hasta que los datos se reciben por completo.¿Puede el código que se ejecuta en un hilo de fondo ser más rápido que en el hilo principal de VCL en Delphi?

Antes de emprender el trabajo de mover todo el código a un hilo de fondo para la prueba, me pregunto si alguien tiene algún conocimiento general en esta área? ¿Cuáles han sido tus experiencias con el código ejecutándose dentro y fuera del hilo principal de VCL? Tenga en cuenta que las mediciones de temporización se realizan cuando no hay absolutamente ninguna actividad activada por el usuario durante las pruebas.

También me pregunto si elevar la prioridad del hilo justo por debajo del tiempo real sería algo bueno. Nunca he visto una gran mejora en mis tiempos de ejecución al experimentar con esas banderas.

- roschler

+1

¿qué está utilizando para medir el tiempo de cada operación? – hatchet

+1

¿cómo estás cronometrando? –

Respuesta

10

Sin un código fuente simple para reproducir el problema, y ​​cómo estás sincronizando tus hilos, será difícil entender lo que ocurre en tu software.

Suena definitivamente como sea:

  • Una cuestión Arquitectura - cómo se definen los hilos?
  • Un problema de medición: ¿cómo estás sincronizando tus hilos?
  • A typical scaling issue del administrador de memoria y la implementación relacionada con la cadena RTL.

Sobre el último punto, considere esto:

  • El administrador de memoria actual (FastMM4) no está reduciendo así la CPU de varios núcleos; intente con un administrador de memoria por subproceso, como our experimental SynScaleMM - nota p. que el equipo del Compilador de Free Pascal ha escrito un nuevo MM de escala desde cero recientemente, para evitar dicho problema;
  • Intente cambiar la implementación del proceso de cadena para evitar la asignación de memoria (use búferes estáticos) y el recuento de referencias de cadena (cada acceso de conteo de referencias de cadena produce un LOCK DEC/INC que no se escala tan bien en la CPU de múltiples códigos; nivel de proceso, utilizando, por ejemplo, PChar en búferes estáticos en lugar de string).

Estoy seguro de que sin operaciones string, encontrará que todos los hilos son equivalentes.

En resumen: ni el actual Delphi MM, ni la implementación actual de cadenas escala bien en la CPU multi-core. Acaba de descubrir un problema conocido del RTL actual. Lee this SO question.

+0

¿El problema de escala no afecta a todos los hilos por igual? es decir: si las operaciones son lentas, ¿son igualmente lentas en todos los hilos, incluida la VCL principal? –

+0

@ A Bouchez, pero ¿entraría en juego el problema FastMM4/multi-core con una aplicación estrictamente de un solo hilo? ¿Qué atributo de la administración de memoria de FastMM4 no está escalando bien en multi-core? –

+0

@ A Bouchez: Leí las publicaciones de tu blog sobre Delphi's LOCK y otros problemas de administración de cadenas, así que ahora puedo ver cómo afectarían el rendimiento de múltiples núcleos, pero ¿cómo se relacionaría eso con una aplicación estrictamente de subproceso único? –

6

Cuando el código tiene el control del hilo de la VCL, por ejemplo si se trata de un método y no llama a ninguna VCL controla o llame Application.ProcessMessages, entonces el tiempo de riego no ser afectado solo porque está en el hilo principal de VCL.

No hay sobrecarga, ya que "posee" toda la potencia de procesamiento del hilo cuando está en su propio código.

Le sugiero que utilice una herramienta de creación de perfiles para encontrar dónde está el cuello de botella real.

+0

La política de desbordamiento de pila consiste en que no firma sus publicaciones porque ya vienen con su nombre y foto policial. Lo edité para ese efecto. –

+0

¿Conoces una buena herramienta de creación de perfiles para Delphi 6? –

+0

Utilicé una versión anterior de AQTime cuando utilicé Delphi 6, pero tiene otras opciones. http://www.torry.net/pages.php?id=1525 –

12

Dado que todos los subprocesos tienen la misma prioridad, como normalmente lo hacen, no puede haber diferencia, por las siguientes razones. Si usted está viendo una diferencia, re-evaluar el código (asegúrese de ejecutar el mismo en ambos hilos VCL y de fondo) y asegurarse de que tienes tiempo, adecuadamente:

  • El compilador genera exactamente el mismo código , no importa si el código se ejecutará en el hilo principal o en un hilo de fondo. De hecho, puede poner todo el código en un procedimiento y llamarlo desde el hilo Execute() y desde el hilo VCL principal.

  • Para la CPU, todos los núcleos y todos los hilos son iguales. A menos que en realidad sea una CPU Hyper Threading, donde no todos los núcleos son reales, pero luego vea la siguiente viñeta.

  • Aunque no todos los núcleos de la CPU son iguales, el hilo se ejecutará muy poco probable en el mismo núcleo, el sistema operativo es libre para moverlo a voluntad (y en realidad programar su hilo para ejecutarse en diferentes núcleos En Diferentes Momentos).

  • La sobrecarga de mensajería no es importante para el hilo VCL principal, porque a menos que llame al Application.ProcessMessages() manualmente, la bomba de mensajes simplemente se detiene mientras el procedimiento realiza su trabajo.La bomba de mensajes es pasiva, su hilo necesita solicitar mensajes de la cola, pero dado que el hilo está ocupado haciendo su trabajo, no está solicitando ningún mensaje, así que no hay gastos generales allí.

Sólo hay un lugar en el que los hilos no son iguales, y esto se puede cambiar la velocidad percibida de ejecución: Es el sistema operativo que los horarios de las discusiones a las unidades de ejecución (núcleos), y para el sistema operativo hilos tienen diferentes prioridades . Puede decirle al sistema operativo que un determinado subproceso debe tratarse de manera diferente con la API SetThreadPriority() (que se utiliza en la propiedad TThread.Priority).

1

¿Estás preguntando si un hilo de fondo sería más rápido? Si el hilo de fondo tiene el mismo código que el hilo principal y no hay nada más en el hilo principal, no puede ganar nada con un hilo de fondo. Los subprocesos se deben usar para dividir y distribuir cargas de procesamiento que, de lo contrario, competirían entre sí y/o se bloquearían entre sí cuando se ejecuta en el hilo principal. Como pareces estar lidiando con un caso donde el hilo principal está inactivo, simplemente generar un hilo para ejecutar el código lento no ayudará.

Los hilos no son mágicos, no pueden acelerar el código lento o eliminar los cuellos de botella de procesamiento en un segmento particular no relacionado con contención en el hilo principal. Asegúrese de que su código no esté haciendo algo que desconozca y de que su metodología de sincronización sea correcta.

Mi primera corazonada sería que su interacción con el socket está afectando su sincronización de una manera que no ha detectado ... (Sé que dijo que está seguro de que eso no está involucrado, pero tal vez vuelva a verificarlo. ..)

+0

Mover el procesamiento grande y largo a los hilos de fondo vale la pena, incluso si el hilo VCL principal está inactivo: Mantiene el VCL receptivo. Si voy a esperar 2 minutos para que termine algo, preferiría ver una ventana receptiva con una barra de progreso, no una ventana que no responda. –

+0

@Mikey. La razón por la que señalé mi uso de tomas de corriente es porque me preocupa que pueda desempeñar un papel, no quise dar a entender que estaba seguro de que no era así, solo señalé que no empiezo a temporizar hasta después de la operación consulta se ha recibido desde el socket. La biblioteca de socket que uso se encuentra en un bucle de mensajes de proceso personalizado. Sin embargo, una vez que se golpea mi código, no llama a los mensajes de proceso y, dado que tiene un solo hilo, no estoy seguro de cómo el bucle de mensajes de la biblioteca de sockets podría afectar las cosas. –

+0

@Cosmin - LOL - eso es lo que dije: "y no hay nada más en el hilo principal ... distribuya cargas de procesamiento que de lo contrario competirían entre sí y/o se bloquearían entre sí cuando se ejecuta en el hilo principal" – Vector

3

El rendimiento no puede evaluarse estáticamente. Para eso, necesitas obtener AQ Time, o algún otro perfilador de rendimiento para Delphi. Uso AQtime, y me encanta, pero soy consciente de que es caro.

Su código no será mágicamente más rápido simplemente porque lo haya movido a un hilo de fondo. En todo caso, su tiempo todo incluido hasta que vea resultados en su UI puede ser un poco más lento, si tiene que enviar una gran cantidad de datos desde el hilo de fondo al hilo de primer plano a través de algunos mecanismos de sincronización.

Sin embargo, si puede ejecutar partes de su algoritmo en paralelo, es decir, dividir su trabajo para que tenga 2 o más subprocesos de trabajo procesando sus datos, y tiene un procesador quad core, entonces su tiempo total para hacer un carga fija de trabajo, podría disminuir. Eso no significa que el código se ejecute más rápido, pero dependiendo de muchos factores, puede obtener un pequeño beneficio del multihilo, hasta la cantidad de núcleos en su computadora. Nunca va a haber un aumento de rendimiento de 2 veces, usar dos subprocesos en lugar de uno, pero puede obtener un 20% -40% de mejor rendimiento, en sus soluciones paralelas de más de un subproceso, dependiendo de cuán escalable sea su montón bajo cargas multiproceso, y cómo IO/memoria/caché vincula su carga de trabajo.

En cuanto a aumentar las prioridades de subprocesos, generalmente lo único que hará allí será alterar el delicado equilibrio del rendimiento de su sistema Windows. Al elevar las prioridades logrará (a veces) un aumento nominal, pero irrepetible y no garantizable en el rendimiento. Dependiendo de las otras cosas que haga en su código y de sus fuentes de datos, jugar con las prioridades de los hilos puede presentar problemas sutiles. Vea el problema Dining Philosophers para más.

Su mejor apuesta para optimizar la velocidad de las operaciones de cuerda es probarla primero y averiguar exactamente dónde está utilizando la mayor parte de su tiempo. ¿Son operaciones de montón? Memoria Copiar y mover operaciones? Sin un generador de perfiles, incluso con el asesoramiento de otras personas, seguirás cometiendo un pecado capital de programación; optimización prematura. Estar orientado a resultados. Ser ciencia basada. Medida. Entender. Entonces decide.

Habiendo dicho eso, he visto un montón de código horrible en mi tiempo, y hay una cosa asesina que la gente hace que mata por completo el rendimiento de la aplicación de subprocesos; Usando TThread.Sincronizar demasiado.

Aquí hay un caso patológico (Extreme), que, lamentablemente, se produce en la naturaleza con bastante frecuencia:

procedure TMyThread.Execute; 
    begin 
     while not Terminated do 
     Synchronize(DoWork); 
    end; 

El problema aquí es que el 100% del trabajo se hace realmente en el primer plano, que no sea el " si terminó "check", que se ejecuta en el contexto del hilo. Para empeorar el código anterior, agregue un modo de suspensión no interrumpible.

Para obtener un código de hilo de fondo rápido, use Sincronizar con moderación o nada, y asegúrese de que el código que llama sea simple y se ejecute rápidamente, o mejor aún, use TThread.Queue o PostMessage si realmente pudiera vivir con el hilo principal en cola actividad.

+0

P - gracias por el consejo de AQTime. Afortunadamente todavía soportan totalmente a Delphi 6. –

+0

@Warren - "Sé basado en la ciencia. Mide. Comprende. Luego decide". - +1 – Vector

Cuestiones relacionadas