c# wcf performance c#-4.0 appdomain

c# - La primera conexión WCF realizada en el nuevo dominio de aplicación es muy lenta



performance c#-4.0 (3)

Tengo una biblioteca que utilizo que usa WCF para llamar a un servicio http para obtener la configuración. Normalmente, la primera llamada toma ~ 100 milisegundos y las llamadas subsiguientes toman solo unos pocos milisegundos. Pero he descubierto que cuando creo un nuevo dominio de aplicación, la primera llamada WCF de ese dominio de aplicación toma más de 2.5 segundos.

¿Alguien tiene una explicación o solución de por qué la primera creación de un canal WCF en un nuevo dominio de aplicación tomaría tanto tiempo?

Estos son los resultados de referencia (cuando se ejecuta sin un depurador adjunto en la versión de 64 bits), observe cómo en el segundo conjunto de números las primeras conexiones tardan 25 veces más.

Running in initial AppDomain First Connection: 92.5018 ms Second Connection: 2.6393 ms Running in new AppDomain First Connection: 2457.8653 ms Second Connection: 4.2627 ms

Este no es un ejemplo completo, pero muestra la mayoría de cómo produje estos números:

class Program { static void Main(string[] args) { Console.WriteLine("Running in initial AppDomain"); new DomainRunner().Run(); Console.WriteLine(); Console.WriteLine("Running in new thread and AppDomain"); DomainRunner.RunInNewAppDomain("test"); Console.ReadLine(); } } class DomainRunner : MarshalByRefObject { public static void RunInNewAppDomain(string runnerName) { var newAppDomain = AppDomain.CreateDomain(runnerName); var runnerProxy = (DomainRunner)newAppDomain.CreateInstanceAndUnwrap(typeof(DomainRunner).Assembly.FullName, typeof(DomainRunner).FullName); runnerProxy.Run(); } public void Run() { AppServSettings.InitSettingLevel(SettingLevel.Production); var test = string.Empty; var sw = Stopwatch.StartNew(); test += AppServSettings.ServiceBaseUrlBatch; Console.WriteLine("First Connection: {0}", sw.Elapsed.TotalMilliseconds); sw = Stopwatch.StartNew(); test += AppServSettings.ServiceBaseUrlBatch; Console.WriteLine("Second Connection: {0}", sw.Elapsed.TotalMilliseconds); } }

La llamada a AppServSettings.ServiceBaseUrlBatch está creando un canal a un servicio y llamando a un solo método. He utilizado wireshark para ver la llamada y solo toma milisegundos obtener una respuesta del servicio. Crea el canal con el siguiente código:

public static ISettingsChannel GetClient() { EndpointAddress address = new EndpointAddress(SETTINGS_SERVICE_URL); BasicHttpBinding binding = new BasicHttpBinding { MaxReceivedMessageSize = 1024, OpenTimeout = TimeSpan.FromSeconds(2), SendTimeout = TimeSpan.FromSeconds(5), ReceiveTimeout = TimeSpan.FromSeconds(5), ReaderQuotas = { MaxStringContentLength = 1024}, UseDefaultWebProxy = false, }; cf = new ChannelFactory<ISettingsChannel>(binding, address); return cf.CreateChannel(); }

Desde la creación de perfiles de la aplicación, se muestra que en el primer caso, construir la fábrica de canales y crear el canal y llamar al método lleva menos de 100 milisegundos.

En el nuevo dominio de aplicación, la construcción de la fábrica de canales tomó 763 milisegundos, 521 milisegundos para crear el canal, 1,098 milisegundos para llamar al método en la interfaz.

TestSettingsRepoInAppDomain.DomainRunner.Run () 2,660.00 TestSettingsRasporp.RepoInAppDomain.AppServSettings.get_ServiceBaseUrlBatch (V9). (string,! 0 &) 2,522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient () 1,371.21 Tps.Core.Settings.ServiceModelModel.IClientChallelllch

EDITAR

Después de usar perfmon con el objeto de carga de .NET CLR puedo ver que cuando carga el segundo dominio de aplicación, carga muchas más clases en la memoria de lo que inicialmente hace. La primera línea plana es una pausa que puse después del primer dominio de aplicación, allí hay 218 clases cargadas. El segundo dominio de aplicación hace que se carguen 1,944 clases en total.

Supongo que la carga de todas estas clases es lo que ocupa todo el tiempo, por lo que ahora la pregunta es: ¿qué clases está cargando y por qué?

ACTUALIZAR

La respuesta se debe al hecho de que solo un dominio de aplicación puede aprovechar las dlls del sistema de imágenes nativas. Por lo tanto, la lentitud en el segundo dominio de aplicación fue que tuvo que rechazar todo el Sistema. * Dlls utilizados por wcf. El primer dominio de aplicación podría usar las versiones nativas predefinidas de esas dlls, por lo que no tenía el mismo costo de inicio.

Después de investigar el LoaderOptimizationAttribute que Petar sugirió, eso parecía solucionar el problema, usando los resultados de MultiDomain o MultiDomainHost en el segundo AppDomain para tomar la misma cantidad de tiempo que la primera vez para acceder a cosas a través de wcf

Aquí puede ver la opción predeterminada, observe cómo en el segundo AppDomain ninguno de los ensamblajes dice Native, lo que significa que todos tuvieron que ser modificados, que es lo que se estaba tomando todo el tiempo

Aquí está después de agregar el LoaderOptimization (LoaderOptimization.MultiDomain) a Main. Puedes ver que todo está cargado en el dominio de aplicación compartido

Aquí está después de que el usuario LoaderOptimization (LoaderOptimization.MultiDomainHost) a main. Puede ver que todos los archivos DLL del sistema están compartidos, pero mis propios archivos DLL y cualquier otro que no esté en el GAC se cargan por separado en cada dominio de aplicación

Entonces, la respuesta es el servicio que hizo esta pregunta mediante el uso de MultiDomainHost, ya que tiene un tiempo de inicio rápido y puedo descargar AppDomains para eliminar los ensamblajes creados dinámicamente que utiliza el servicio


¿Tienes un proxy HTTP definido en IE? (tal vez un script de configuración automática). Esto puede ser una causa.

De lo contrario, supongo que es el tiempo que se tarda en cargar todas las DLL. Intente separar la creación de proxy de la llamada de actull al servicio, para ver qué se está demorando.


Encontré el siguiente artículo que habla de cómo solo el primer dominio de aplicación puede usar dlls de imágenes nativas, por lo que un dominio de aplicación infantil siempre será forzado a JIT un montón de cosas que el dominio de aplicación inicial no tiene por qué. Esto podría llevar al impacto de rendimiento que estoy viendo, pero ¿sería posible de alguna manera no obtener esta penalización de rendimiento?

Si hay una imagen nativa para el ensamblaje, solo el primer dominio de aplicación puede usar la imagen nativa. Todos los demás AppDomains tendrán que compilar JIT el código que puede resultar en un costo de CPU significativo.


Puede decorar su atributo Main con LoaderOptimization para decirle al cargador CLR cómo cargar clases.

[LoaderOptimization(LoaderOptimization.MultiDomain)] MultiDomain - Indicates that the application will probably have many domains that use the same code, and the loader must share maximal internal resources across application domains.