2012-04-18 11 views
7

Me gustaría pasar un float [] a un método de C. La firma C parece:¿Pasar un flotador [] como ref flotar al código no administrado es una buena idea?

EXTERN int process_raw(float *inBuffer, float *outBuffer); 

en C# la firma es:

public static extern int process_raw(ref float inBuffer, ref float outBuffer); 

va a ser problemático para pasar matrices con una referencia al primer elemento:

process_raw(ref someArray[0], ref anotherArray[0]) 

gracias!

EDITAR: Por supuesto, es importante saber qué hace el código C con los flotadores: los tratará como matrices y leerá valores de inBuffer y escribirá valores en outBuffer. Como se analiza a continuación, la pregunta es si toda la memoria se fijará durante la llamada PInvoke.

EDIT 2: Otro comentario. Elegí el flotador ref a propósito, porque yo también quería hacer cosas como:

fixed(byte* outBuff = buffer) 
{ 
    Process(ticks, ref aFloat, ref ((float*)outBuff)[0]); 
} 

En este caso no debería ser un problema, porque el puntero se fija de todos modos, pero la pregunta para la gama normal se mantiene por encima.

+1

Tenga en cuenta que debe considerar el rediseño de API para incluir tamaños de búferes de "entrada" y "salida". Especialmente si existe la posibilidad de que el tamaño del buffer "out" tenga una longitud diferente a la del buffer "in". También facilitará la clasificación. –

+0

en este caso es claro, el método toma por definición 64 muestras. pero hay otros métodos como este que tienen un argumento de tamaño, pero los omite por simplicidad. mi única preocupación es el GC y si la memoria se fijará. el código C leerá 64 valores de inBuffer y escribirá 64 valores en outBuffer. – thalm

Respuesta

4

No hay auto-pin involucrado en p/Invocar. P/Invoke se realiza estrictamente a través de marcándose! (sin código inseguro) Mariscal significa asignar memoria (no administrada) y copiar. Debajo de las cubiertas probablemente haya un pin durante la duración de la copia, pero no para la duración de la llamada de función nativa.

Si tiene que pasar una matriz de 64 carrozas dentro y fuera de una función nativa tiene dos opciones:

  1. Marshall llevarlo a cabo.
  2. Utilice un código inseguro para fijar y pasar la memoria administrada directamente.

Aquí es el método marshalled:

[DllImport(...)] 
private extern static int process_raw([In] float[] inBuffer, [Out] float[] outBuffer); 

Tenga en cuenta que he añadido el [En] y [Fuera] atributos que le dicen al Marshaller (A) no copiar a la salida y (Out) no copiar en el camino hacia adentro. Es una buena idea considerar siempre esos atributos al escribir una declaración ap/invoke.

Aquí es el método inseguro:

[DllImport(...)] 
private extern static unsafe int process_raw(float * inBuffer, float * outbuffer); 

public static unsafe int Process(float[] inBuffer, float[] outBuffer) 
{ 
    // validate for null and Length < 64 
    fixed (float * pin = inBuffer) 
    fixed (float * pout = outBuffer) 
     return process_raw(pin, pout); 
} 

comentario Expanded

Es mi entendimiento de que la Marshaller es capaz de "bajo ciertas circunstancias" optar por el pin de la memoria administrada en lugar de asignar no administrado memoria y copia El problema con eso es: ¿qué circunstancias?

No sé la respuesta, pero tengo una sospecha: cuando la DLL nativa es ciertas DLL del sistema. Eso es solo una suposición.

Lo que esto significa para usted y para mí es bastante simple: siempre comience con el método ordenado. Si tiene problemas de rendimiento y el generador de perfiles le dice que la llamada nativa está consumiendo una gran parte del tiempo, puede probar el método inseguro y volver a crear un perfil. Si no hay una mejora significativa, entonces lo único que se espera es optimizar la llamada nativa.

+0

¡gracias! Entonces, ¿cómo escribiría la firma si quiero exponer ambas posibilidades? para que el programador pueda usar array o punteros como argumentos? ¿Necesitará dos implementaciones? – thalm

+2

@Tergiver: como optimización, Marshaller fijará en lugar de copiar tipos blittables, lo que incluye matrices 1D. – user7116

+0

Ver mi comentario ampliado arriba. @thalm: No entiendo la pregunta. Tienes ambas firmas arriba. – Tergiver

4

Es un poco ambiguo lo que estás tratando de hacer aquí. El código nativo podría tratar el float* como un puntero a float o una matriz de valores float.

Si el código nativo cree que es un puntero a un solo float, entonces su código está bien. Tener el float ser un ref en administrado y el puntero en nativo se alineará correctamente.

Si el código nativo cree que es una matriz de valores float, es muy probable que tenga un problema. Esto funcionará en el caso de esquina que lo trata como una matriz de longitud 1. Para cualquier otra longitud aunque es necesario utilizar una matriz en la signatura logrado

public static extern int process_raw(float[] inBuffer, float[] outBuffer); 
+0

+1, agregaría que el enfoque actual de OP requeriría que las matrices se anclasen manualmente ya que el Marshaller no entiende su intención. (borré mi respuesta) – user7116

+0

@sixlettervariables CLR auto-pin cualquier memoria que se pasa a la capa PInvoke durante la duración de la llamada. El pin solo debería ser necesario si el puntero nativo sobrevive a la llamada de PInvoke – JaredPar

+0

Estaba bajo la impresión de que los elementos de la matriz no activaban ese comportamiento (o creían que los problemas de P/Invoke estaban relacionados con ese tipo de código). – user7116

Cuestiones relacionadas