Corrupción de punto flotante en un entorno multihilo C

Tengo un problema con un valor de punto flotante devuelto por sumas y multiplicaciones que terminan siendo inválidos.

Antecedentes: estoy usando Speex en un entorno de subprocesos múltiples de Visual Studio. Y en cierto punto, generalmente después de 1 o 2 minutos de codificación y decodificación de audio, mi señal decodificada se vuelve completamente Nan. Creo que mi problema es el mismo que se discutió en este hilo (Speex en windows, corte de audio), pero profundicé un poco más en este tema.

Situación: Modifiqué una parte de libspeex para poner un código de depuración, esto es lo que tengo (he ampliado algunas macros aquí, sé que algunas partes son redundantes).

float *mem, *den;     // Arrays of finite float values
float nyi;      // finite float value.
float a1, a2;   // debug test variables.

...

if (!_finite(mem[j]) || !_finite(mem[j+1]))
    printf("Nan\n");       // Does not reach this

a1     = ((mem[j+1])+(float)(den[j])*(float)(nyi));   // a1 == expected value
mem[j] = ((mem[j+1])+(float)(den[j])*(float)(nyi));   // mem[j] == -1.#IND
a2     = ((mem[j+1])+(float)(den[j])*(float)(nyi));   // a2 == expected value

if (!_finite(mem[j]) || !_finite(mem[j+1]))
    printf("Nan\n");          // Program reach this and stops at breakpoint

El primer comportamiento extraño es que a1 y a2 calcular el valor correcto mientras miembro[j] no es. Segunda cosa rara: Si trato de volver a ejecutar la afectación a miembro[j] declaración (sé que podría dar lugar a resultados inesperados, pero aún da una pista para fines de depuración), entonces el valor afectado a miembro[j] es el valor esperado: Igual que a1 y a2.

Comprobé lo obvio:

  • Esta parte del código está protegida por mutex: no hay forma de que otro subproceso haya dañado la memoria.
  • Todos los valores flotantes son válidos, finitos y el resultado de la suma y la multiplicación debe estar dentro del rango de un flotante.
  • Todos los índices de matriz están dentro del rango de sus respectivas matrices.

El problema no parece aparecer si no hay otro subproceso en ejecución.

  • Este hilo: el hilo de decodificación de audio.
  • Un hilo de codificación de audio.
  • Algunos subprocesos de socket de red...

Es parte de un software grande, pero la parte de decodificación está realmente protegida del resto por los mutex adecuados.

Por lo tanto, parece que se produjo un cambio de contexto en medio del cálculo flotante y que no pudo restaurar el contexto después. Pero es difícil creer que algo tan malo pueda pasar.

Escuché sobre inconsistencias de punto flotante cuando se usa en subprocesos múltiples, pero solo debería afectar la parte menos significativa, no generar un valor Nan.

¿Alguien ha visto alguna vez tal comportamiento? ¿Cómo lo resolviste?

preguntado el 27 de julio de 12 a las 14:07

Así que estas tres líneas están en la propia biblioteca, ¿verdad? ¿Por qué hay tres líneas calculando lo mismo? -

¿Por qué también estás repitiendo tu cálculo 3 veces? ¿No podría almacenarlo una vez y asignarlo varias veces? ¿Es esto estrictamente reproducible? (¿Falla en el mismo lugar cada vez, o sucede en diferentes momentos?) -

@Shahbaz Las líneas a1 y a2 las agregué para depurar y mostrar el comportamiento no repetitivo/extraño. -

@Wug No falla en la misma iteración cada vez, toma entre 1 y 2 minutos. Pero falla en la misma línea de código cada vez que falla. -

@jslap, ¿cuál es el valor de j, y el tamaño de mem para el cual la prueba falla? Además, ¿has notado que den no es realmente una matriz? (¿o es un error tipográfico al copiar para desbordar la pila?) -

1 Respuestas

Preguntas:

  • ¿Qué pasa con todos los moldes superfluos?
  • cuales son los valores de den[j] y nyi?

Aparte de eso, una posibilidad razonable es que otro cálculo en el mismo hilo haya desbordado la pila de punto flotante o haya usado instrucciones MMX pero no haya podido emitir el emms instrucción antes de ceder el control (cualquiera de estas condiciones hará que los cálculos de coma flotante no objetables produzcan resultados de NaN). Comience examinando la palabra de estado x87 en el estado de falla para confirmar o descartar estas posibilidades.

El hecho de que el problema no ocurra sin varios subprocesos hace que esta explicación sea menos probable, pero un estado x87 corrupto es, con mucho, la fuente más común de NaN "inexplicables de otro modo", y debe descartarse primero.

Respondido 27 Jul 12, 15:07

¿Debo ver el registro de estado antes o después de que ocurriera la falla? ¿Tengo que ver la parte de excepción? ¿Cuál sería el valor normal para los bits de excepción? 0? - bofetada

Comprobar el estado del registro de estado una vez que se ha producido el fallo. Si los bits IE y SF (0 y 6) están configurados, entonces está viendo daños en la pila. Si IE (bit 0) está configurado pero SF no, entonces su cálculo produjo legítimamente un NaN. - Esteban Canon

Pido disculpas por la demora. Volví a reproducir el error y el registro de estadísticas está establecido en 0x0961. Entonces, sí, la operación no válida y los bits de falla de pila están configurados. Entonces mi pila está dañada. ¿Alguna idea de cómo proceder desde allí? - bofetada

Interesante. El estado de la FPU es local del subproceso, por lo que esto no debería cambiar dependiendo de si se está ejecutando o no otro subproceso (a menos que su sistema operativo esté realmente dañado, en cuyo caso esperaría ver otras fallas). ¿Hay algún código que se ejecute solo si el subprocesamiento múltiple está habilitado? La otra cosa que debe verificar es asegurarse de compilar el proyecto con advertencias de prototipos faltantes habilitados; llamar a funciones que devuelven un valor flotante sin un prototipo es, con mucho, la fuente más común de corrupción de la pila x87. - Esteban Canon

No faltan prototipos, en ninguna parte. Compruebo los bits de estado justo antes de la operación defectuosa, y está bien. Inmediatamente después, establece los dos bits defectuosos: IE y SF. El desmontaje se ve bien. Y si restablezco IE y SF y repito la operación flotante, da el resultado correcto y no configura IE o SF. - bofetada

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.