2010-07-29 16 views
9

¿Qué se puede hacer para acelerar la llamada a métodos nativos desde el código administrado?Optimizar llamadas administradas a nativas

Estoy escribiendo un programa que necesita para poder administrar listas de objetos de tamaño arbitrario y recuperar información de ellos a alta velocidad, que se alimenta a los scripts. Los scripts son bits del código compilado C#. Estoy escribiendo una capa de interfaz básica desde el DLL/SO/etc de C++ (nativo) a la capa de gestión C# (.Net o Mono).

Ahora, he estado haciendo algunas pruebas, y he encontrado que, en promedio, Invocar un método nativo desde código administrado es algo así como 100 veces más lento que hacerlo todo administrado (todos nativos y todos administrados son idénticamente) rápido, para referencia).

La sintaxis que estaba usando es:

[DllImport("test.dll")] 
extern static public String test_method(String value); 

String returnedValue = test_method("hello world"); 

¿Hay una manera de almacenar en caché un puntero a la función, un código de invocador rápido, que aumentaría la velocidad después de cargar la biblioteca nativa? Eso resolvería el problema bastante bien, así que dudo que exista. : P

Edit: No especifiqué, pero esto debe funcionar en Windows, Linux (Ubuntu al menos) y Mac OS X, todos para x86 y x64. De lo contrario, me habría ido con una interfaz C++/CLI y habría terminado con eso, pero a menos que eso funcione para las 3 plataformas, no puedo usarlo.

+1

Quizás haya alguna conversión de cadenas y juegos de caracteres que ralentiza esto. ¿Obtiene una relación de rendimiento similar si mide funciones con, por ejemplo,int en lugar de cadena? – x4u

+2

No quiero mencionar lo obvio, pero lo haré desde 100x parece fuera de línea en mi experiencia, pero no está ejecutando compilaciones de depuración \ y/o con el depurador conectado al hacer la medición de perfunción en el código .Net? –

+0

@chibacity: De hecho, fue una versión de depuración con el depurador adjunto. Aunque todos los componentes, nativos y gestionados, se depuraron con depuradores. Voy a ejecutar mi número de nuevo con compilaciones de lanzamiento y verifico la diferencia. – ssube

Respuesta

3

En relación con mi pregunta comentario, hemos establecido que se trataba de una depuración construir con el depurador asociado. Esto tiene un impacto masivo en el rendimiento en tiempo de ejecución del código .Net. Fácil error de hacer. :)

Supongo que con una compilación de lanzamiento y sin un depurador adjunto, la diferencia de rendimiento ahora es mucho más razonable.

Si tiene una API muy hablador, y los métodos nativos que se invocan son baratos, entonces la sobrecarga de llamada al método puede ser un problema de rendimiento. Intenta diseñar una API menos hablante. Esta es una técnica típica utilizada para aumentar el rendimiento de las comunicaciones de límites \ sistemas.

Si el rendimiento es aceptable después de ordenar el problema del depurador, hay una técnica simple que he utilizado para obtener fácilmente un aumento sustancial en el rendimiento de las API habladas, simplemente agregando un solo atributo.

En las clases donde tiene sus funciones importadas (es decir, las funciones DllImport), coloque el atributo SuppressUnmanagedCodeSecurity en las clases. Esto eliminará algunas costosas comprobaciones de seguridad de cada llamada de P/Invoke. Consulte la documentación en SuppressUnmanagedCodeSecurity para comprender las ramificaciones de esto. Tiendo a mantener mis funciones importadas agrupadas en clases internas (que solo contienen funciones importadas) con este atributo aplicado.

+0

Curiosamente, una compilación de lanzamiento (en todos los lados) sin depurador tiene el mismo rendimiento 1: 100. Estaba intentando probar la sobrecarga de interoperabilidad, así que diseñé una función que compila hasta 4/5 instrucciones de asm (una cmp, una jmp, dos mov y una ret). En cuanto a crear una API menos hablante, parece que eso es lo que haré. Algo similar a los cambios mínimos de estado de un controlador, el almacenamiento en memoria intermedia de cierta información, posiblemente escribiendo en un búfer compartido en la memoria no administrada y llamando a una función de sincronización de vez en cuando. Gracias por el consejo de seguridad. – ssube

3

Tal vez la cadena de clasificación es lo que está causando una desaceleración. Para fines de comparación, intente perfilar una función que tome y devuelva tipos elementales de C++ como int.

También puede intentar experimentar con C++/CLI. De esa forma, puedes tomar el control explícito de la clasificación y quizás ver una mejora.

En C++ montaje/CLI:

System::String^test_method(System::String^args) 
{ 
    pin_ptr<const wchar_t> pp = PtrToStringChars(args); 
    //This might leak, probably needs a smart pointer to wrap it 
    wchar_t* ret = native_method(pp); 
    return gcnew String^(ret); 
} 
+0

Debería haber especificado, pero a menos que eso funcione para Linux y Mac, desafortunadamente está fuera de discusión. Mi primera opción habría sido una interfaz C++/CLI, por lo que su respuesta es correcta, ya que planteé la pregunta, pero olvidé especificar multiplataforma y no he visto ninguna manera de hacer eso para otros sistemas operativos que Windows. : \ – ssube

+0

Bugger, que definitivamente descarta C++/CLI. –