Cómo escribir un código C# muy rápido | Mark Farragher | Skillshare

Velocidad de reproducción


  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Cómo escribir un código C# muy rápido

teacher avatar Mark Farragher, Microsoft Certified Trainer

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Lecciones en esta clase

23 lecciones (3h 43min)
    • 1. Consejos de rendimiento de C#

      2:59
    • 2. Introducción a la optimización de códigos

      6:12
    • 3. ¿Qué es la memoria de pilas?

      5:23
    • 4. ¿Qué es la memoria del montón?

      6:18
    • 5. ¿Qué son los tipos de valor?

      5:50
    • 6. ¿Qué son los tipos de referencia?

      5:29
    • 7. ¿Qué es el boxeo y el unboxing?

      6:28
    • 8. ¿Qué son las cuerdas inmutables?

      6:43
    • 9. Un curso de choque en el idioma intermedio

      14:45
    • 10. Consejo #1: evitar el boxeo y desconectar

      9:39
    • 11. Consejo #2: agregue cadenas juntas de manera eficiente

      8:20
    • 12. Consejo #3: usa la clase de lista correcta

      8:29
    • 13. Consejo #4: usa el tipo de matriz correcto

      9:09
    • 14. Consejo #5: evita lanzar excepciones

      14:55
    • 15. Consejo #6: usarlo para el lugar de foreach

      16:40
    • 16. ¿Cómo funciona el recolector de basura?

      16:07
    • 17. Consejo #7: optimiza la recolección de basura

      18:26
    • 18. Consejo #8: usa delegados rápidos

      9:13
    • 19. Consejo #9: construye una fábrica de clase rápida

      17:06
    • 20. ¿Valen los problemas los arreglos en la pila de la gente?

      11:28
    • 21. ¿Cómo uso los punteros en C#?

      10:05
    • 22. Consejo #10: acelerar el código con punteros

      11:43
    • 23. Recapitulación del curso

      1:44
  • --
  • Nivel principiante
  • Nivel intermedio
  • Nivel avanzado
  • Todos los niveles

Generado por la comunidad

El nivel se determina según la opinión de la mayoría de los estudiantes que han dejado reseñas en esta clase. La recomendación del profesor o de la profesora se muestra hasta que se recopilen al menos 5 reseñas de estudiantes.

193

Estudiantes

--

Proyectos

Acerca de esta clase

78e5abec

¿Sabes cómo escribir un código C# rápido?

Quizás ya hayas inscrito en un curso de programación en C# o hayas aprendido el idioma en la escuela o la universidad. Pero aquí hay un hecho sobrio: la mayoría de los cursos solo te enseñan a escribir código, no a escribir código rápido.

El . El marco neto es enorme. Para cualquier problema dado, hay muchas soluciones, y no siempre está claro qué solución es la mejor opción.

¿Sabías que agregar cuerdas juntas de forma incorrecta ralentizará tu código con un factor de más de doscientos? Y si no manejas las excepciones de la manera correcta, tu código funciona mil veces más lento de lo normal.

El código de C# lento es un gran problema. El código lento en la web no se escalará a miles de usuarios. El código lento hará que tu interfaz de usuario sea inutilizable. El código lento hará que tus aplicaciones móviles languidezcan en la tienda de aplicaciones.

¡El código lento te retendrá!

Puedo ayudarte.

En una serie de conferencias cortas cubriré muchos cuellos de botella de rendimiento común. Presentaré cada problema y luego escribiré un pequeño programa de pruebas para medir el rendimiento de la línea de base. Luego demostraré cada solución posible, y evaluaré cómo se miden cada solución.

¡Pero hay más! También me sumergiré en Common Intermediate Code (CIL), el lenguaje que el compilador C# compila. Si esto suena abrumador, ¡no te preocupes! El lenguaje de CIL es realmente muy fácil de leer y comprender. Te llevaré a través de los conceptos básicos en una conferencia rápida de 15 minutos.

Poder leer el código CIL es una habilidad muy útil que te ayudará a evitar muchas dificultades de rendimiento y te dará una comprensión más profunda de la misma. Marco neto.

¿Por qué deberías tomar este curso?

Deberías tomar este curso si eres un desarrollador principiante o intermedio de C# y quieres llevar tus habilidades al siguiente nivel. Todas mis conferencias son muy fáciles de seguir, y explico todos los temas con código claro y muchos diagramas instructivos.

O quizás estés trabajando en una sección crítica de código en un proyecto C# y necesitas que tu código funcione lo más rápido posible. Los consejos y trucos de este curso te ayudarán inmensamente.

¿O quizás te preparas para una entrevista de trabajo relacionada con C#? Este curso te dará una excelente base para responder a cualquier pregunta relacionada con el rendimiento que te presenten.

Conoce a tu profesor(a)

Teacher Profile Image

Mark Farragher

Microsoft Certified Trainer

Profesor(a)

Mark Farragher is a blogger, investor, serial entrepreneur, and the author of 11 successful Udemy courses. He has been a Founder and CTO, and has launched two startups in the Netherlands. Mark became a Microsoft Certified Trainer in 2005. Today he uses his extensive knowledge to help tech professionals with their leadership, communication, and technical skills.

Ver perfil completo

Calificaciones de la clase

¿Se cumplieron las expectativas?
    ¡Superadas!
  • 0%
  • 0%
  • Un poco
  • 0%
  • No realmente
  • 0%
Archivo de reseñas

En octubre de 2018, actualizamos nuestro sistema de reseñas para mejorar la forma en que recopilamos comentarios. A continuación, se muestran las reseñas escritas antes de esa actualización.

¿Por qué unirse a Skillshare?

Toma las galardonadas clases originales de Skillshare

Cada clase tiene lecciones cortas y proyectos prácticos

Tu membresía apoya a los profesores de Skillshare

Aprende desde cualquier lugar

Toma clases sobre la marcha con la aplicación Skillshare. Transmite o descarga para verlas en el avión, el metro o donde aprendas mejor.

Transcripciones

1. Consejos de rendimiento C#: Déjame hacerte una pregunta. ¿ Te gustaría convertirte en arquitecto de rendimiento C agudo? Vale, tengo que admitirlo, yo hice esa palabra. Pero en mi libro, un arquitecto de desempeño afilado C es un desarrollador senior que escribe abrigo afilado C de alto rendimiento . Por lo que un desarrollador senior que está agudamente consciente de las técnicas de optimización off coat sobre derechos, llamó a juegos para el análisis de datos para la adquisición de datos en tiempo real para todos estos entornos fríos donde la capa rápida es esencial, Así que ¿te gustaría convertirse en un arquitecto de rendimiento C agudo? ¿ Te gustaría unirte al club entonces? Este es el curso para ti. En este curso, te voy a enseñar una lengua fuera de los hacks de optimización. Vamos a ver la optimización básica. Por lo que esta es la fruta colgada baja, los sencillos retoques que harán que tu código se ejecute hasta 1000 veces más rápido. Bueno, mira la optimización de los intermediarios. Por lo que estos son anillos reflejos bastante avanzados fuera de tus códigos. Eso te dará una pequeña mejora en el desempeño. Vamos a ver alguna optimización realmente avanzada es vamos a ver el uso punteros en C agudo en escribir directamente en la memoria en algunos escenarios. Esto es más rápido que usar las clases .net estándar. Miraremos la fundación fuera del punto net runtime, así que te daré un curso crash en la pila en el montón. Cómo tipos de valor y tipos de referencia de tiendas en la pila, en el montón sobre cómo se mueven los datos entre la pila en el montón. Cuando estemos corriendo abrigo, te enseñaré en el lenguaje de los medios, así que en realidad tomaré el cumplimiento de los códigos en un lenguaje intermedio. Muéstrale ese lenguaje intermedio a ustedes, y les explicaré qué están haciendo todas las instrucciones. Por lo que para el final del curso, podrás leer lenguaje intermedio sobre Te darás cuenta de cómo compiló este compilador de edición . Tiendas abrigo en lenguaje intermedio sobre qué tan ciertas instrucciones voy pueden ralentizar tu costa. Por lo que este es un curso bastante grande. Contiene montones y montones de conferencias ahí cuestionarios para poner a prueba tus conocimientos. Se puede descargar el código fuente que he estado usando para benchmarking. Entonces, ¿te gustaría convertirte en arquitectos de performance C Sharp? Entonces este es el curso para ti. Así que he creado este curso para nivel medio o senior ver desarrolladores de tiendas que quieran aprender a escribir rápido y eficiente capa afilada C para tener su carrera lista para temas geniales como desarrollos de juegos o adquisición de datos en tiempo real. Gracias por escuchar. Espero poder darle la bienvenida en el curso. 2. Introducción a la optimización de códigos: Entonces hablemos de optimización de códigos. ¿ Qué es la optimización de código? Bueno, optimización del recuento en general implica modificar en el sistema I t para que funcione de manera más eficiente o utilizar menos recursos o ser más robusto. Por ejemplo, un programa informático puede optimizarse para que se ejecute más rápido o use menos memoria o almacenamiento en disco o sea más receptivo en términos fuera de la interfaz de usuario. Optimizamos el código para evitar desaceleraciones inaceptables. El código ganador está en uso. Imagina hacer clic en un botón etiquetas mostrar informe y luego tener que esperar 10 minutos antes de que nada aparezca en la interfaz de usuario aceptable. El retraso es de dos segundos, por lo que necesitamos encontrar la forma de generar ese informe en sólo dos segundos. También optimizamos para hacer escalables nuestros códigos. Personalmente he experimentado sitios web que funcionan bien con 10 usuarios simultáneos pero descomponen por completo cuando 50 usuarios pasaron a visitar el sitio web todos al mismo tiempo. Incluso hay un nombre para ello. Se llama los nombres de efecto de barra hija para cuando los sitios web populares slash DOT cuenta con un sitio web en su primera página, que luego se bloquea rápidamente porque millones de visitantes hacen clic en el hipervínculo al sobrecargar. El servidor Web, una canción su conocimiento fuera de la optimización del código va a ayudar a remolcar a evitar estos desastres potenciales . Entonces en este curso, vamos a buscar. Así es como hacer que un programa se ejecute más rápido. Tenemos un número de estrategias off a nuestra disposición. Para lograr este objetivo, podemos reescribir algoritmos para lograr el mismo resultado. Con menos frío, podemos evitar instrucciones innecesarias. Podemos asegurarnos de que las bibliotecas que utilizamos estén específicamente diseñadas para la tarea que nos ocupa. Podemos reducir el consumo de memoria tanto como sea posible, y podemos evitar escenarios donde el código está bloqueando, esperando un recurso lento. La optimización del desempeño en este curso todos caen en una o más de estas cinco categorías. Pero antes de que empecemos, una última palabra de advertencia. Hay una famosa citas de Michael Jackson sobre me refiero al científico informático británico Michael Jackson, no al otro tipo. El presupuesto va así. El primer descarte optimización del programa es. No lo hagas. El segundo cuarto fuera de la optimización del programa, que es sólo para expertos, es no lo hagas todavía. El famoso informático Donald's Neuf tenía una ropa similar. La optimización prematura es la raíz de todo mal. El pensamiento detrás de estas citas es que tu código irá completamente a ayudar. Si intentas enfocarte en la optimización del rendimiento desde el principio mientras aún estás trabajando en tu programa, tu pelaje es fluidos y evoluciona en direcciones inesperadas. Es imposible predecir de manera confiable de antemano dónde estarán los cuellos de botella de rendimiento. Aquí te dejamos un ejemplo cerca del final de este curso, aprenderás que los punteros pueden acelerar tu abrigo, pero solo si los usas de una manera muy específica. Así que imagina re factor en todo tu programa para usar punteros, y luego, un par de semanas después, necesitas reflexionar sobre tu algoritmo y hacia abajo. El punto de optimización ya no funciona. Entonces ahora estás atascado con abrigo inseguro, difícil de leer que en realidad es más lento que bien escrito, limpio y seguro abrigo. Eso no usa punteros, así que probablemente vas a tener que deshacer todo. Otro ejemplo. Pasaste semanas exprimiendo cada onza posible de rendimiento de una función de biblioteca, y eventualmente lograste optimizar la función para que se ejecutara en menos de un milisegundos. Enhorabuena, Entonces tu programa evoluciona de nuevo, y para cuando entras en producción, tu mensaje se llama 99% del tiempo, justo después de un núcleo de base de datos que tarda 300 milisegundos en completarse. Entonces toda tu optimización zwart de Nada. lo que el curso de acción recomendado es escribir sencillo, claro en código modular on Deja la organización hasta el final cuando sepas de manera confiable dónde van a estar los cuellos de botella de rendimiento. No obstante, existen excepciones a esta regla. Te dejaré con la versión completa fuera de la cita de noticias de Donald. Debemos olvidarnos de pequeñas eficiencias, digamos alrededor del 97% del tiempo. La optimización prematura es las raíces del mal. obstante, no debemos dejar de lado son oportunidades en ese crítico 3%. Entonces si puedes ver claramente el 3% justo desde el principio, siéntete libre de comenzar a optimizar de inmediato. 3. ¿Qué es la memoria de stack?: pila de memoria era simplemente la pila es un bloque de memoria fuera que se utiliza para llamar a métodos y almacenar variables locales. Entonces déjame dibujar eso aquí en la pizarra. Dibujaré la pila como una columna vertical así. No, cuando empiezo a ejecutar algún código, esa pila inicialmente estará vacía. Pero cuando Mike Oates llama a los métodos esto sucede, los parámetros del método la dirección de retorno en todas las variables locales fuera de los métodos se ponen en la pila. Todo este bloque de datos se llama stack frame. Entonces, ¿qué pasa con la pila cuando mis códigos llaman a otro método desde dentro de este método, que esto lo mismo sucede otra vez? Los parámetros del método devuelven la dirección en variables locales fuera del nuevo método que pongo en la pila justo encima del marco de pila anterior. Entonces por eso se llama pila. La información se apila uno encima del otro. ¿ Qué pasa cuando mi culto encuentra declaración de retorno? Como probablemente sabrás, una declaración de retorno termina un método y regresa al código de llamada. Ahora en la pila. Esto es lo que pasa. elimina todo el marco de pila que correspondía al método, pero podría estar pensando ¿qué pasó con todas las variables locales que se almacenaron en el marco de la pila? Bueno, todos salen fuera de alcance, lo cual es sólo una forma elegante de decir que están destruidos. Entonces este es un hecho importante recordar el momento en que regresas fuera de los métodos todas tus variables locales fuera de que los métodos salgan de alcance y se destruyen. Si sigo con mi programa y también regreso de los primeros métodos, estamos de vuelta a donde empezamos con una pila vacía. Podría estar preguntándose qué sucede si un método llama a otros métodos, que causan otro método, que llama a otros métodos 1000 veces bien, la pila se llenaría rápidamente con marcos de pila hasta que haya espacio normal. Eventualmente, el stock estará completamente lleno en el marco neto DOT lanza pila. Excepción de desbordamiento. Si ves este mensaje de error, significa que tienes una secuencia infinita de llamadas de método off en algún lugar de tu código. Echemos un vistazo a algún código. Escribí un programa sencillo para dibujar un cuadrado en la pantalla. Ves que tengo un cajón métodos cuadrados que llama a un método de línea de cajón cuatro veces para dibujar los cuatro lados de un cuadrado. Pondré un descanso, puntos dentro del cajón, linear métodos y luego correr mi frío. Vigila esto. Ahora, en este punto de mi abrigo, esa pila se verá así. Mi primera llamada para dibujar cuadrado es aquí en la parte inferior de la pila con sus cuatro parámetros, dirección de retorno y variables locales. A continuación se presenta la llamada a una línea de cajón con nuevamente cuatro parámetros. Dirección de devolución el. En este caso, no hay variables locales porque la línea de cajón no tiene ninguna en estudio visual. Puedes echar un vistazo a esta pila abriendo la vista de pila de llamadas, que está aquí. Se puede ver la llamada en dibujar cuadrado y luego en una línea de cajón. Por lo que esta ventana te muestra qué marcos de pila se almacenan en la pila. Ahora mismo, como demostración final, permítanme mostrarles una pila. Excepción de desbordamiento. Déjame modificar mi abrigo así. He modificado mi abrigo. Entonces ahora dibuja una línea llama a una línea de cajón, que luego llama a una línea de cajón que llama a una línea de cajón. Obtienes la foto. Esta secuencia nunca terminará. Cuando ejecuto este programa, creará una secuencia infinita fuera de línea dibujada métodos llamándose a sí mismos Ejecutemos el programa y veamos qué sucede. Yo estoy ahí lo tienes. El stack es de tamaño limitado. Si tengo demasiados métodos llamando a todos los métodos, eventualmente la pila estará completamente llena en el marco de red DOT fluye la excepción de desbordamiento de pila . Entonces, ¿qué hemos aprendido? There'll Net Framework utiliza la pila para realizar un seguimiento de los médicos cada vez que se llama al mensaje todos sus parámetros de método. La dirección de retorno en todas las variables locales se colocan en la pila. Todo este bloque de memoria se llama marco de pila. Cuando regresa de un método, elimina el marco de la pila superior. Todas las variables locales salen de alcance en este punto, y destruí. Si tienes una secuencia infinita de métodos off, llamando a todos los métodos el stock se llenará por completo hasta que la ortiga arroje una pila. Excepción de desbordamiento 4. ¿Qué es la memoria heap?: el otro tipo de memoria dentro de una computadora se llama memoria hit o simplemente el montón. Echemos un vistazo a la siguiente línea de código Ahora. Cada vez que las palabras clave nuevas aparecen en línea, estabas creando un objeto en el montón. Esta es una regla fundamental. Los objetos de Internet siempre se crean en el montón. Nunca van a la pila. Por lo que he hecho algunos cambios en mi programa de cajón cuadrado. Echemos un vistazo. Anteriormente, los métodos de línea de cajón esperaban cuatro parámetros enteros para dibujar una línea. Pero ahora tengo un cajón Métodos de polígono, que espera una matriz fuera de línea objetos en dibuja todo de una sola vez. El método draw square configura una matriz de líneas con cuatro objetos correspondientes a los cuatro lados de la plaza y luego las llamadas dejan caer todo un arma para dibujar todo en uno. Ve ahora recuerda, en mi viejo programa cuadrado de cajón, puse unos puntos de ruptura dentro de los métodos de línea de cajón y cuando ejecuté mis códigos, la pila se veía así. Pero ahora he hecho muchos cambios en el programa. Entonces, ¿cómo se va a ver ahora la pila en el montón? De acuerdo, entonces imagina que pongo unos puntos de descanso dentro del cajón. Métodos poligonales al ejecutar mi programa. El stack en el montón entonces se vería así. El parámetro frías líneas existe en la pila porque es un parámetro, pero lo inicializé con las nuevas palabras clave. Por lo que la matriz en sí se crea en el montón, por lo que la variable en esta pila se refiere a un objeto en el montón. También puedes ver que la matriz de líneas tiene cuatro elementos y que cada elemento se refiere a un objeto de línea en otro lugar del Now, Si estás pensando que todo esto se ve mucho más complicado que el culto anterior, entonces estás debajo de la derecha pista on. Esto me lleva a unos puntos importantes. Este código, que utiliza una bodega, es ligeramente más lento que mi pelaje anterior, que usaba enteros para todo. Y esto se debe a que se tienen que calcular todas las referencias adicionales, desde el parámetro de líneas en la pila hasta la matriz. Instancia, en el montón, las manos de una matriz elementos del montón a una instancia de objeto de línea en otro lugar del montón. Por lo que la principal toma, por ahora es códigos que usan enteros es un poco más rápido que el código que utiliza objetos. Entonces ahora qué sucede cuando termine el método del polígono de dibujo. Ese marco de pila se retira en las luces, parámetro sale del alcance y se destruye. Pero aquí hay algo que podrías no haber esperado. Z array en la línea. Los objetos en el montón siguen existiendo ahora. Esta es una situación interesante. El parámetro leones está fuera de alcance, pero los objetos en el calor siguen ahí. Decimos que los objetos son de referenciados por la variable o parámetro, que se refieren a ellos sin alcance. Los objetos de referencia siguen existiendo y no se destruyen de inmediato. Por lo que aquí hay otro importante para llevar. El marco neto DOT siempre pospondrá. Limpiar los objetos referenciados en esto es porque limpiar el montón lleva mucho tiempo posponiéndolo el mayor tiempo posible. Tu código en realidad se ejecutará más rápido, pero eventualmente el marco tendrá que limpiar el montón o rápidamente nos quedaríamos sin memoria. Este proceso de limpieza se llama recolección de basura, y ocurre periódicamente en segundo plano. Cuando el marco inicia la recolección de basura. Identifica todos los objetos en el montón, que ya no son referenciados por ningún parámetro variable u objetos en tu pelaje en su de asigna cada uno de ellos. Volveremos a la recolección de basura en conferencias posteriores, y te daré algunos consejos sobre cómo evitar problemas de rendimiento en tus códigos debido a recolección de basura. Entonces, ¿qué hemos aprendido? Cada vez que usaste las nuevas palabras clave en tus códigos, estás creando un objeto en el montón. La variable en sí puede vivir en la pila, pero se referirá a un objeto en el montón. El nuevo programa draw square que utiliza una matriz de líneas es ligeramente más lento que el programa antiguo que usaba enteros en. El motivo de esto es que todas las referencias de objeto extra necesitan ser procesadas cuando los parámetros y las variables locales en la pila salen del alcance. No se destruyen los objetos correspondientes del montón. Siguen existiendo en un estado D referenciado. El siguiente marco pospone la limpieza de los objetos referenciados en el montón por el mayor tiempo posible por razones de rendimiento. Pero eventualmente el marco iniciará un proceso llamado recolección de basura. A medida que de asigna todos los objetos referenciados en la cadera 5. ¿Qué son los tipos de valor?: en la conferencia anterior, nos enteramos de esta pila en el calor. El conocimiento que ganes te ayudará a ir hacia adelante cuando veamos las variables, cómo se almacenan en la memoria por el marco de punto net y cómo esto afecta ahora el rendimiento de tu código. Anteriormente, cuando hablé de esta pila, mostré algún código con el cajón. Linus. Es que usó cuatro parámetros enteros. Echemos un vistazo más de cerca a un entero en el marco de Dark Net. El tipo insurgente forma parte de una clase especial de tipos llamada tipos de valor, pero ¿cuál es el tipo de valor? El tipo de valor es un tipo de variable, donde el tipo del valor fuera de la variable almacenada conjuntamente. Entonces si tengo una variable entera local con un valor de descuento 12 cientos y 34 ese tipo de entrevista en su valor se almacenará juntos así. Entonces echemos un vistazo a esta pila otra vez. Anteriormente, cuando hablé de esta pila, mencioné todos los tipos de datos que los almacenados en la pila son parámetros de mensaje. La dirección de retorno de un mensaje suena variables locales. Entonces, si tengo una variable local fuera de tipo entero con el valor apagado 12 cientos y 34 se almacenaría en la pila así. ¿ Ves eso? El tipo y el valor almacenados juntos en la pila. Ahora ten esto en cuenta porque en la próxima conferencia voy a hablar de tipos de referencia, que es el almacenamiento de manera diferente. Por lo que podría estar preguntándose qué tipos en el marco de red oscura son en realidad tipos de valor. Bueno, aquí hay una lista completa. Todos los tipos numéricos son tipos de valor, incluyendo todos fuera de los puntos flotantes enteros en tipos decimales. También, Booleano en operaciones y estructuras son tipos de valor. Cualquier otra cosa en el framework dot net se llama tipo de referencia, que voy a discutir en breve. Cuando las personas tratan de explicar la diferencia entre un tipo de valor y un tipo de referencia, a menudo se escucha la siguiente explicación. Un tipo de valor es un tipo que existe en la pila. No obstante, este es el valor de la habitación. Los tipos pueden existir tanto en la pila en el aquí, permítanme demostrar. Anteriormente, cuando hablé del montón, mencioné qué tipo de datos se almacenan en el montón. ¿ Te acuerdas de lo que waas? Fue todo objetos, instancias creadas con unas nuevas palabras clave en c sharp. Entonces imagina que creo un objeto en el montón usando las nuevas palabras clave en mi abrigo en este objeto tiene uno entierrado tu campo que contiene el valor 12 cientos. 34. Ahora este entero se almacenará así. Entonces verá, sé que tienen el tipo de valor en el montón por lo que los tipos de valor pueden existir tanto en la pila en el día en los tipos de valor de propiedad definitoria no es donde se almacenan, sino que el valor se almacena junto con el tipo. Entonces déjame terminar mostrándote a importancia características adicionales o tipos de valor. Digamos que tengo un mensaje en mis códigos con dos variables A y B. Ambas se introducen. La variable A contiene el valor 1234 bajo la variable B es cero. Ahora lo que pasa cuando lo asigne será Echa un vistazo a esto. El valor de A se copia en be. Ahora esta es una característica importante de los tipos de valor sus signos por valor, lo que significa que su valor se copia sobre. Entonces ahora ¿qué pasa si comparo A y B? Se trata de dos variables diferentes que simplemente sucede que contienen el mismo valor. Entonces cómo lo hará. El marco dot net interpreta bien la situación así. El marco considera que estas dos variables son iguales. Esta es la segunda característica importante fuera de los tipos de valor. comparan por valor, lo que significa que dos variables diferentes que sostienen el mismo valor se consideran iguales. Entonces, ¿qué hemos aprendido? Los tipos de valor almacenan su valor directamente, junto con los tipos de valor de tipo pueden existir en la pila en y un salto tipos de valor son un signo por valor, lo que significa que el valor se copia sobre el valor. Los tipos se comparan por valor. consideran iguales dos variables con el mismo valor. 6. ¿Qué son los tipos de referencia?: En la conferencia anterior, hablé de los tipos de valor y mencioné brevemente a su contraparte el tipo de referencia. Pero, ¿qué es un tipo de referencia? Bueno, un tipo de referencia es un tipo de variable que se refiere a un valor almacenado en el montón. No antes. Cuando hablé del montón, mostré mi programa de dibujo cuadrado modificado que tenía un método de dibujar un polígono, si recuerdas con un parámetro de matriz de líneas, Así que dibujar polígono esperaba una matriz fuera de línea objetos. Echemos un vistazo más de cerca a esos objetos de línea solo para refrescar tu memoria. Aquí está mi abrigo otra vez. Aquí se puede ver la definición fuera de la clase de línea. Se trata de un simple contenedor de datos con dos conjuntos de coordenadas. Entonces imagina que tengo un mensaje con una variable local fuera de línea de tipo. ¿ Cómo se vería eso? Un recuerdo bien, así. Se puede ver que la variable en sí está en la pila, pero se refiere a una línea Objetos en el pero ¿también puede existir una variable de tipo de referencia en el montón? Sí, seguro. Todo lo que necesito hacer es crear un objeto en el montón. El uso de las nuevas palabras clave giró a la mitad que los objetos tienen un campo fuera de línea de tipo. Ahora el recuerdo entonces se verá así. ve que ahora tienen una variable de tipo de referencia en el montón, y se refiere a una línea de objetos, que también se almacena en el qué en otro lugar, por lo que para resumir. Los tipos de referencia pueden existir en la pila el día en el montón, igual que los tipos de valor, pero siempre se referirán a un valor en el montón. Déjame terminar mostrándote a importancia características adicionales fuera de los tipos de referencia. Digamos que tengo un mensaje en mis códigos con dos variables A y B. Ambas variables son líneas. La variable A se refiere a una instancia de línea en el montón bajo la variable B se dice que sabe. Ahora qué pasa cuando le asigne a a B. Revisen ustedes mismos La referencia off A se copia en ser. Esta es una característica importante fuera de los tipos de referencia. Se asignan por referencia, lo que significa que la referencia se copia sobre. Terminas con dos variables, refiriéndose a la misma instancia de objeto en la cadera. Entonces ahora ¿qué pasa cuando comparo A y B? Se trata de dos variables diferentes que hacen referencia a los mismos objetos en el montón. ¿ Cómo interpretará bien esta situación el marco de punto net. El marco considera que estas dos variables son iguales. Pero espera, ¿qué pasa con este escenario para hacer referencia a variables de tipo que apuntan a dos objetos separados en el montón. Pero ambos objetos contenían datos idénticos. ¿ Cómo interpretará el marco dot net esta situación Sonidos bebedor. El marco considera que estas dos variables no son iguales, por lo que esta es otra característica importante fuera de los tipos de referencia. comparan por referencia, lo que significa que dos variables diferentes referidas a los mismos objetos se consideran iguales. Pero dos variables diferentes, refiriéndose a dos objetos separados pero idénticos, se consideran no iguales. Entonces, ¿qué hemos aprendido? Los tiempos de referencia se pueden percibir al almacén de tipos de referencia sin valor, una referencia a su valor, y este valor siempre se almacena en la referencia. Los tipos pueden existir en esta pila el día en el, pero su valor siempre se almacena en la referencia. Los tipos se asignan por referencia, lo que significa que la referencia se copia sobre la referencia. Los tipos se comparan por referencia. consideran iguales dos variables que hacen referencia a los mismos objetos, y dos variables que se refieren a objetos separados pero idénticos se consideran no iguales 7. ¿Qué es el boxeo y el unbox?: en esta conferencia, les voy a mostrar un misterio. Echa un vistazo a este código. Este es un programa muy sencillo. Empecé con la variable A que contenía el valor 1234. Entonces declaro una segunda variable. Estar fuera de los objetos de tipo. Andi. Asigné a a B en C. Sharp. Todos los tipos heredan de objetos, incluidos los enteros, por lo que puedes poner básicamente todo en una variable de tipo de objeto. Pero esperar en los jurados son tipos de valor, y los objetos son tipos referenciados. Entonces en la memoria, mis variables se almacenan así. Aquí está mi variable entera A con su valor off 1234. Y aquí está mi variable de objeto B. Pero B es un tipo de referencia, y hemos aprendido en la conferencia anterior que los tipos de referencia siempre se refieren a un valor en el montón aquí. Esto no es posible porque A es una variable local, por lo que existe en la pila encontró que es un tipo de valor, por lo que su valor también se inicia en el stock. Simplemente no hay forma de que B se refiera a a a porque ambas variables recitan en diferentes tipos de memoria. Un tipo de referencia nunca puede referirse a la memoria de pila, por lo que este programa nunca puede funcionar. ¿ Verdad? Bueno, éste sí. Vuelvo a diciembre en el estudio Andi, ejecuto el programa. Aquí vamos. Enfriar. En realidad funciona. Pero, ¿cómo es eso posible? Ahora, esto es raro. En base a lo que aprendimos en conferencias anteriores, este programa no debería funcionar. Pero aún así lo hace. ¿ Cómo es eso posible averiguarlo? Déjame de obligar a este programa a un lenguaje intermedio. Al examinar las instrucciones del lenguaje intermedio, podríamos encontrar una pista. Y aquí está. Mira esto. Instrucción de idiomas intermedios llamados libros. ¿ Se puede adivinar qué hace aquí? Dibujaré en la pizarra. Aquí están los diseños de memoria con las dos variables A y B. Ahora la instrucción de libros hace esto. Entonces para hacer que el programa funcione, el marco Net realmente copia el valor del entrevistador de la pila, luego gravar el valor en los objetos en él Coloca estos objetos en el para que la variable B pueda entonces referirse a ella. Todo este proceso se llama boxeo. El boxeo ocurre cada vez detrás de bambalinas. Cuando tienes un parámetro variable campos o propiedad fuera de los objetos de tiempo y asignas un tipo de valor a su boxeo es agradable porque tipo de desdibuja la línea entre los tiempos de valor y tipos de referencia. Pero el boxeo también puede ser un dolor porque es introduce gastos generales adicionales en tu código. Ahora quizá te estés preguntando si hay un proceso correspondiente llamado unboxing. Sí, lo hay. Aquí está mi programa otra vez. Echemos un vistazo a la línea final. Declaro una variable, veo fuera de tipo entero y asigno el valor del objeto a su usando un tipecast. Otro poco de magia. En realidad, porque ver, existe en la pila y ser los objetos se refiere a un objeto en el en el lenguaje intermedio . Aquí está la instrucción correspondiente. Se llama Unbox. Déjame volver a la pizarra sobre Dibujar el proceso de Unboxing. Empezamos desde la situación de caja con el entero en el Ahora. Entonces esto sucede. Desboxeo desempaqueta el entero en el montón y copia el valor de nuevo en la variable Ver en la pila. desboxeo ocurre cada vez detrás de bambalinas cuando tienes un valor de objeto y lo arrojas a un tipo de valor. boxeo y el unboxing pueden afectar seriamente el rendimiento de tu abrigo, así que asegúrate de evitarlo tanto como sea posible en secciones de misión crítica. Te compartiré algunos consejos y trucos en conferencias posteriores sobre cómo hacer esto. ¿ Qué hemos aprendido? boxeo es el proceso fuera tomando tipos de valor en la pila, empacándolos en objetos como colocando estos objetos en el montón. El boxeo ocurre siempre que asignas tipo de valor a un parámetro variable de campo o propiedad fuera de objetos de tipo. Unboxing es el proceso inverso. Los objetos fuera del montón se desempaquetan en el valor. tipos dentro se copian de nuevo a la pila. Aprende el boxeo sucede siempre que no tienes valor objeto y lo arrojas dedo del pie un tipo de valor. boxeo y el unboxing afectaron negativamente el rendimiento fuera de tu abrigo y se debe evitar en secciones de misión crítica. 8. ¿Qué son las cadenas inmutables?: en esta conferencia. Vamos a echar un vistazo a la clase de cuerdas en dot net ahora. ¿ Cuál crees que es una cadena de tipo de valor o un tipo de referencia? Averiguémoslo. Escribí a través de códigos para probar lo que es una cadena en realidad. Echa un vistazo. Empiezo declarando Variable de cadena a al inicializar Es al valor ABC. Entonces declaro una segunda variable de cadena B y un signo A a B. En la siguiente línea, agrego un solo carácter al final del stream be y finalmente escribo ambas cadenas a la consola. Ahora, si las cadenas son tipos referenciados, la pila y el montón se verían así. Tendría que variables A y B, ambas apuntando a los mismos objetos de cadena en el montón. Entonces, si modifico la variable de cadena B, la cadena invariable A también se modificaría porque ambas variables se refieren a la misma cadena. No obstante, si las cadenas son tipos de valor, el segundo montón se vería así. Tendría que variables a y B que contienen cadenas separadas cuando modifico la variable de cadena B. La otra cadena invariable A no se ve afectada. Qué escenario es correcto. ¿ Crees Vamos a averiguarlo ejecutando mi código. Aquí vamos y ahí lo tienes. Las cadenas son obviamente tipos de valor, ¿verdad? Bueno, no hay cadenas en realidad son tipos referenciados en la memoria. Las cosas se ven así. Aquí están las dos variables A y B, ambas refiriéndose a la misma cadena en el montón. Pero algo especial sucede cuando modifico la variable de cadena B en lugar de modificar la cadena en el montón directamente. Esto sucede. Nosotros decimos, que las cuerdas son inmutables. Objetos en la Red. Cualquier modificación a una cadena da como resultado que se cree una nueva cadena en el flujo original se deja intacta. Debido a esto, las cadenas se comportan como si fueran tipos de valor. Entonces, ¿cuáles son los beneficios de tener cadenas inmutables amenazas seguridad, cuerdas inmutables? ¿ Una amenaza segura? Porque no se pueden modificar los ahorros de memoria. Las cuerdas idénticas se pueden fusionar de forma segura. Esto se llama al convertir primera asignación para copiar una cadena. Todo lo que necesitas hacer es copiar la referencia en lugar de tener que copiar todos los caracteres sobre uno por uno. Y las cadenas pasantes de primera comparación se pueden comparar comparando la referencia en lugar de tener que comparar todos los caracteres uno por uno. Entonces echemos un vistazo a los códigos intermedios para ver qué está pasando entre bastidores. Coloco un punto de descanso en la línea final fuera de mi programa. Inicia el programa y luego cambia a códigos intermedios. Aquí está la primera línea. Una cadena con contenido. ABC se carga en el uso de la instrucción de cadena de carga en tiendas invariables A con una instrucción de ubicación de tienda . Zen A se asigna a ser simplemente cargando la referencia invariable A con una ubicación de carga en almacenarlo a la variable B con una ubicación de tienda, una asignación de cadena superrápida por referencia exactamente lo que esperaríamos para un tipo inmutable . El mágico sucede cuando modifico la cuerda invariable estar detrás de bambalinas. El framework llama a un método conmocionado de cadena. Déjame mirar ese método usando el navegador de montaje en el estudio de veraneo. Aquí está. Se puede ver que el canalla de gato es crea una nueva cuerda aquí. A continuación, copia ambos argumentos de cadena en la nueva cadena y devuelve aquí una referencia a la nueva cadena. Por lo que ahí lo tienes. En cambio, off modificar esta cadena invariable ser los códigos crea una corriente completamente nueva, copia todo en su y almacena una referencia a la nueva cadena. Invariable Be the old string in B se deja en el montón en un estado de acuerdo referenciado a la espera ser basura recolectada. Todos los métodos de la clase de cadena que modifica la cadena de cualquier manera tiene este comportamiento. En lugar de apagarse modificando directamente el arroyo. Crea una cadena completamente nueva y coloca las modificaciones. Ahí dentro. El string original se deja intacto. Entonces, ¿qué hemos aprendido? Las cadenas son tipos referenciados en e inmutables. Debido a esto, las cadenas se comportan como si fueran tipos de valor. Son signos y se comparan por valor. Las cuerdas inmutables son amenazas seguras en las cuerdas musicales son rápidas porque pueden firmarse y compararse por referencia. Las cadenas inmutables guardan memoria porque las cadenas idénticas se pueden fusionar entre sí en la memoria. 9. Un curso intensivo en el idioma intermedio: en esta conferencia, voy a hablar de lenguaje intermedio. Entonces, ¿qué es exactamente el lenguaje intermedio? Bueno, déjame empezar mostrándote un compilador clásico, en este caso un C o C plus, más un compilador. En la parte superior. Fuera de las pizarras, se ve una simple pieza de código, un bucle de cuatro que agrega un entero a una variable resultados fríos. A, C o C plus compilador tomaría estos fragmentos de capa fuente y lo compilaría al lenguaje de máquina, que básicamente es solo un montón de números almacenando memoria en ejecutada por la CPU en tu computadora pero un darknet compilador. Polvar las cosas de manera diferente. Echemos un vistazo a la misma pieza de códigos escrita en C. Sharp. Un compilador de C agudo primero compilará la costa de origen a un lenguaje intermedio especial llamado Bueno, adivinaste este lenguaje intermedio común. C i, l o seda. El mismo abrigo se almacena en un archivo dll o xer. Cuando se ejecuta el código, otro compilador inicia. Este compilador se llama compilador jit, o G i T G. I T significa justo a tiempo. El compilador se ejecuta justo a tiempo en los últimos momentos posibles antes de que los códigos tengan que estar equivocados. El compilador Jit toma el lenguaje intermedio y lo compila el lenguaje de la máquina del dedo del pie. Entonces, ¿por qué este complicado proceso de compilación de dos pasos? Bueno, porque en realidad tiene un número fuera ventajas importantes. En primer lugar, el abrigo de seda se puede optimizar para la plataforma en la que se está ejecutando. Por lo que las diferencias entre, por ejemplo, un M D en Intel. uso de la CPU puede ser aprovechado por completo por el compilador jit para crear un lenguaje de máquina rápido y optimizado . Segundos. El frío pronto es totalmente portátil ya que no está ligado a una plataforma de hardware en particular. El abrigo se puede ejecutar en Windows en Lenox en computadoras Apple. De hecho, creé todo este curso en un MacBook Pro de Apple y todos los ejemplos de código. Vas a ver un funcionando de forma nativa en OS X, pero podrías tomar estos exactamente mismos bols ejecutados que estoy ejecutando, copiarlos a leaners o windows y ejecutarlos ahí. Funcionaría. Otra ventaja útil off silk coat es que puede ser muy pelea antes de correr para asegurarse de que el abrigo no realice ninguna operación peligrosa y finalmente, todavía frío se puede anotar con metadatos, por ejemplo, instrucciones de serialización diciendo frío externo exactamente cómo convertir tus clases dedo del pie XML y espalda. ¿ Hay algún inconveniente en usar abrigo de seda? Bueno, sí. Usar una copulación de dos pasos es ligeramente menos eficiente que compilar directamente desde los códigos fuente hasta el lenguaje de máquina. Tan compilado. El Net resfriados es ligeramente más lento que C o C más colillas de código directamente compiladas. Los compiladores le han causado realmente bueno en el último par de años, y así esta diferencia se ha vuelto microscópicamente pequeña. Hoy en día es casi imposible hacer a mano un lenguaje máquina de culto que sea más eficiente que lo que produce el compilador C Sharp. Entonces veamos el lenguaje intermedio con más detalle. ¿ Cómo funciona realmente el lenguaje? El lenguaje intermedio se basa en tres conciertos. El primero es la secuencia off instrucciones que se muestran en las escrituras en la pizarra. Los programas de lenguaje intermedio contienen, o los sets off instrucciones que se ejecutan en secuencia, igual que un programa agudo de C. El local variables en un programa de lenguaje intermedio, un almacenado en ranuras especiales llamadas ubicaciones. Hay un número fuera de ubicaciones disponibles. He dibujado cuatro en la pizarra de la izquierda. Por último, hay una evaluación Stack a stack es una colección fuera de valores con dos operaciones básicas, un push llamado Load in Ill, que agrega valor a la pila y empuja todo lo demás hacia abajo por un nivel, el otro operación es un pop llamado Store in Ill, que elimina el valor superior de la pila en movimientos llenos. Otros valores subiendo un nivel. Hay tres grupos fuera de instrucciones en instrucciones de lenguaje intermedio que empujaron datos sobre la evaluación. Instrucciones pegadas que realizan operaciones en la pila en instrucciones que pop valores de la pila. Entonces echemos un vistazo. Ese es un programa muy sencillo. Echa un vistazo a las siguientes dos líneas de código inicialicé un entero con el Valor 456 y luego agrega uno a ese valor. ¿ Cómo se vería este programa en lenguaje intermedio? Pues bien, este programa se compone de sólo cuatro instrucciones de lenguaje intermedio. El primer instrucción es cargas constantes. Uno, que carga el valor entero de cuatro bytes con signo. Uno en la evaluación atascado nota que es que solo hay una variable local en mi programa llamada I. Y así se almacena en la ubicación cero, con el valor inicial apagado 456. El siguiente instrucción es cargas Ubicación cero, que carga el valor de la variable I en la ubicación cero en la evaluación atascada. Por lo que ahora tenemos dos números de la acción uno y 456. El tercer instrucción es Tía, que suma los dos primeros números en la pila juntos. Por lo que el resultado es 457 que va en la pila y reemplaza a los dos números originales. Entonces ahora tenemos un solo número en la pila. 457. El inicio final es la tienda Localización cero, que saca el valor máximo del stock de evaluación y lo almacena en la Ubicación cero, cual corresponde a la variable I. Aquí hay un par de otras instrucciones que podrías encontrar cuando estás mirando abrigo C afilado compilado. Los libros y las instrucciones de descaja hacen exactamente lo que cabría esperar. Encajan y luego libros tipos de valor en la instrucción B y E significa rama, no igual. Se salta a una diferencia Abrigo de ubicación. Si los dos números principales en el stock de evaluación no son iguales, cool y llamado virtual, llame estático en miembros de clase no estáticos, cargue elementos y almacene elementos carga y almacene elementos en una carrera dimensional. Nueva matriz como nuevos objetos crea una nueva matriz, respectivamente. El nuevo objeto en el retorno regresa de un método y tiro lanza una excepción. Por lo que podría haber pensado que el lenguaje intermedio es muy complejo, pero en realidad es bastante simple. El lenguaje no es tan difícil de entender, y en próximas conferencias iban a echar un vistazo a compilar, ver lotes de olor de Shark y analizar cómo la compiladora traduce su código fuente afilado a lenguaje intermedio sobre lo que el implicaciones en el desempeño son. Echemos un vistazo al sencillo programa. Echa un vistazo a mi código aquí. Empiezo con el número dos y luego uso este bucle cuatro para calcular el 1er 16 poderes de dos multiplicando repetidamente el número por dos. Entonces, ¿cómo se ve esto en lenguaje intermedio? Bueno, todo lo que tengo que hacer es establecer unos puntos de descanso al final del programa aquí, ejecutar mi programa y luego cambiar a vista de desmontaje así. Ahora estamos mirando los compila capa de lenguaje intermedio anotado con líneas del código fuente original para que pudiéramos encontrar fácilmente los potros compilados para una línea dada fuera de C códigos afilados. Entonces empecemos con la declaración fuera de la variable numérica. Aquí se ve una constante de carga a la instrucción, que empuja el número dos en la pila y luego una ubicación de tienda cero, que almacena el número en la ubicación cero. Por lo que sabemos que la ubicación cero corresponde a la variable numérica. Después viene el foro. Ahora presta mucha atención a las etiquetas de direcciones aquí porque los códigos se muestran fuera de servicio para que sea más fácil de leer. El primer paso es inicializar la variable I 20 Así que tenemos una constante de carga cero encendido y la ubicación de la tienda una instrucciones aquí. Entonces ahora sabemos que la variable I está almacenada en la ubicación uno. Entonces el culto salta a la ubicación 14. Ahí tenemos una ubicación de carga uno, que es la muy bien. Cargo Constante 16 que carga el número 16 encendido, luego una instrucción BLT. Ahora BLT significa rama menos que on. Saltará a la ubicación A. Si la variable I es menor a 16. Ubicación A contiene el cuerpo principal fuera del bucle. Carga la variable numérica en la pila. Después carga el número dos en la pila. Multiplica ambos números en la pila juntos y almacena los resultados de nuevo en la variable de número porque el código está fuera de servicio. realidad, la siguiente instrucción después de la ubicación F está aquí en la Ubicación 10. Estamos de vuelta donde la variable I es incremento ID ha probado. Si está por debajo de 16 fue la variable. Alcanzo el valor 16. Vamos a completar a través de la instrucción BLT aquí. El siguiente ubicación después de la ubicación 17 es uno. Ver en la instrucción de retorno regresa fuera del método, y ahí lo tienes. Un programa agudo de cinco líneas C compila a sólo 17 instrucciones de lenguaje intermedio sobre el frío compilado fue bastante fácil de leer, así que felicitaciones que ahora son capaces de leer compilado. Ver abrigo de tiburón. Ser capaz de leer e interpretar códigos de idiomas intermedios es una habilidad muy importante, que te ayudará mucho cuando estés optimizando el rendimiento de tu abrigo. Entonces, ¿qué hemos aprendido? C. Sharp Coats compila a intermedio Language Coat, que luego es compilado de nuevo por un compilador justo a tiempo. Dos lenguaje de máquina Gits Copulation puede optimizar los códigos para el hardware local en los códigos de idioma de los medios , es portátil y puede ejecutarse en múltiples plataformas como windows, Linux y Mac. Puede ser muy lucha por la corrección antes de correr, y el entrenador se puede anotar con metadatos extra, por ejemplo, para guiar la serialización. Intermedio Language utiliza ubicaciones para almacenar variables locales y utiliza una pila de evaluación para realizar operaciones sobre datos. Intermedio Language ha construido en soporte para la creación de objetos que llaman a métodos en el acceso a campos. El lenguaje intermedio ha construido en apoyo para crear y manipular el levantamiento unidimensional . 10. Consejos #1: evita el boxeo y el boxing: anteriormente, te dije que el boxeo y el unboxing afectan negativamente el rendimiento de tu código, y debes evitarlo siempre que sea posible, sobre todo en secciones de misión crítica. Pero, ¿qué tan malo es la sobrecarga de rendimiento fuera del boxeo y el unboxing? ¿ Es peor la preocupación? Averigamos que escribí un programa para medir la diferencia en el rendimiento entre el método que utiliza sólo inter jobs versus y los métodos que hacen exactamente lo mismo usando una variable de objeto . Aquí está el código. Tengo dos métodos. Medir la medida de un hombre B. El 1er 1 medido a toma un entero y le suma el valor uno un 1,000,000 veces. Utilizo un dedo de clase de cronómetro medir con precisión el número apagado milisegundos que tarda en ejecutar el bucle. El segundo mensaje Medida B, hace exactamente lo mismo pero ahora usa una variable de tipo de objeto en lugar de un entero. Todo lo demás es igual. Un millón de repeticiones sobre Estoy sumando el número uno durante cada bucle. Se oracion. Si me desplazo hacia abajo a los métodos principales, ves que empiezo llamando a ambos métodos de medición y descartando los resultados. Hago esto porque podría haber un inicio de retraso en mi abrigo que pudiera sesgar los resultados de medición. Entonces para eliminar ese retraso, ejecuto la prueba completa. Una vez descubiertos los resultados en, luego vuelve a ejecutar la prueba. Por último, visualizo los resultados en milisegundos en la consola para ambos métodos. Estoy asumiendo que ese es el método A será más rápido. Entonces también muestro cuántas veces el Método B es más lento que el método A. No, ya te dije boxeo y uno boxeo introduce un desempeño significante. Overheads en tu frío. Entonces averigüemos de cuánto gastos generales estoy hablando. Déjame ejecutar el programa ahora. Ahí lo tienes. El mensaje A, que utiliza una variable entera, toma 10 microsegundos. El método B, que utiliza una variable de objeto, toma 55 microsegundos. método B es 5.5 veces más lento que el Método A. Echemos un vistazo al código intermedio. Empezaré con la suma de enteros en Método A. Si dije dos puntos de ruptura y ejecutaré, entonces cambie a la vista de desmontaje. Aquí vamos. Ves que el código intermedio está anotado con código fuente C agudo, por lo que es realmente fácil localizar la línea que queremos. En este caso. El agregado en Método A. Aquí está Ahora, antes de que explique qué hacen las instrucciones del mismo, tenga en cuenta que los códigos intermedios utilizan una pila especial de evaluación para realizar cálculos. Números y variables se cargan en esta pila usando instrucciones de carga, comenzando con LD en los resultados se almacenan de nuevo en variables con instrucciones de tienda comenzando por S t La adición en mi código va a ser una secuencia off load on en tienda en capa intermedia . Entonces aquí están las cuatro instrucciones. El 1er 1 ubicación de carga. Se carga la primera variable local, que resulta ser la variable A en la pila de evaluación. Después viene la instrucción de constante de carga uno, que carga el número uno hasta la pila de evaluación. La instrucción add agrega los dos primeros números en la pila juntos, en este caso, el valor de un bajo número uno. Y es reemplaza estos dos números con los resultados de la adición. Por último, la ubicación de la tienda, una tiendas de instrucción. El agregado resulta en la variable A. Ahora veamos la misma línea en el mensaje. Estar usando la variable de objeto. Aquí está otra vez. Empezamos con la ubicación de carga uno, que carga la variable de objeto A hasta la pila de evaluación. Ahora ten en cuenta que una variable de objeto es un tipo de referencia, por lo que ahora tenemos una referencia a un objeto en el montón en el stock de evaluación. Por lo que la siguiente instrucción tiene que estar en instrucción unbox porque los objetos en el calor necesitan ser desempaquetados en el valor interior necesita ser copiado hasta que la pila de evaluación. Después viene la familiar constante de carga uno, que carga el número uno y luego la instrucción de agregar para sumar los dos valores juntos. No a es una variable de objeto, por lo que los resultados de la adición deben empaquetarse en un solo objeto y colocarse en el montón . Por lo que la siguiente instrucción tiene que ser box, que hace exactamente eso en lugares, una referencia a estos nuevos objetos en el montón en la evaluación atascado. Por último, tenemos ubicación de tienda uno que almacena el nuevo objeto de referencia invariable día. Entonces método un método ser contenido más o menos el mismo abrigo, una constante de carga de ubicación baja, luego un anuncio y finalmente, una ubicación de tienda. Pero el Método B necesita una instrucción adicional de unbox on box porque estamos realizando en entierro. Su adición sobre el tipo de referencia de capaz en el marco de red DOT necesita mover datos entre esta pila y el montón para que esto funcione. Por lo que la diferencia de rendimiento se debe enteramente a estas dos instrucciones caja en Unbox. Evitar el boxeo en el unboxing en tu propio código es fácil. Simplemente evita el tipo de objeto. Pero, ¿sabías que el marco Net está lleno de clases que usan objetos u objetos? Arreglos para almacenamiento interno, por ejemplo, Prácticamente todo el sistema lo hace. Colecciones nombre espacio utiliza objeto una carrera internamente, así que manténgalo alejado de las siguientes clases en código Mission Critical. Otra clase popular es la tabla de datos en las tablas de datos de punto del sistema almacenan el valor de cada fila en una matriz de objetos. Incluso las tablas de datos de tipos no son seguras de usar porque usaban moldes de tipo en la parte superior de esa misma matriz de objetos. Por lo que para mantener rápido tu código, considera usar clases de colección genéricas en el sistema. ¿ Las colecciones no dan espacio de nombres genéricos? O si conoce el número fuera de elementos de antemano, considere usar una matriz de tipos simple, unidimensional para tablas de datos. No hay una alternativa simple. Si pudieras salirte con la tuya, re factorizar tus códigos para usar un lector de datos porque son mucho más rápidos que una tabla de datos. Pero recuerde, una tabla de datos son los resultados de una operación de base de datos, que podría tardar muchos, muchos milisegundos en completarse. No hay muchos puntos optimizando la recuperación de datos que viene después. A menos que estuvieras haciendo millones de operaciones el mismo día, la tabla se objeta. Entonces, ¿qué hemos aprendido? Colocar objetos, Variables a tipos de valor? Introduce en la instrucción unbox en capas intermedias de almacenamiento de tipos de valor en variables de objeto introduce una instrucción de caja en códigos intermedios. Códigos con libros en libros corre hasta cinco veces más lento que el mismo código sin el's a instrucciones, debes evitar lanzar hacia y desde un objeto en misión Código crítico. Debe evitar el uso de clases de colección no genéricas en Código de misión crítica el. Y debes evitar usar tablas de datos en códigos de misión crítica, pero solo si realizas muchas operaciones en la misma tabla de datos. Objeto 11. Consejos #2: añade cadenas de manera eficiente: en esta conferencia, quiero echar un vistazo más de cerca a la concatenación de cadenas o cómo agregar cadenas. Juntos, hay dos maneras de sumar cuerdas juntas. El primero es el que usamos apropiadamente todos por todo el lugar en nuestros códigos. Aprendimos a encadenar variables juntas usando el operador plus. Echemos un vistazo a algún código. He hecho una aplicación para medir el rendimiento fuera de sumar cadenas juntas. Empiezo con una cadena vacía aquí y luego en este bucle, agrego un solo carácter a la cadena 10,000 veces. No, Si ejecuto el programa, puedes ver el rendimiento aquí en la ventana de salida en milisegundos. Pero hay otra forma de sumar cuerdas juntas. El framework de masa Net proporciona una clase especial llamada constructor de cuerdas para cuerdas de construcción de pozos . Si escribo el mismo abrigo usando un constructor de cuerdas, comenzando con una cadena vacía, agregando un solo carácter 10,000 veces el código se ve así más o menos el mismo exceso Yo usando los métodos de plumas en lugar del operador plus. Pero el efecto es el mismo en sumar cuerdas juntas. No, permítanme poco comunes los códigos de medición para este segundo método en y ejecutar de nuevo el programa para que podamos comparar los resultados. Chicken hace concatenación de cadenas usando el operador plus toma 242 milisegundos, pero los mismos códigos usando un constructor de cadenas tardan solo un milisegundos. El constructor de cuerdas es 242 veces más rápido. Ahora tengo que hacer un poco de descargo de responsabilidad. Cuando repitas este experimento en tu propia computadora, verás tiempos más rápidos, probablemente en el rango de cientos y 50 a 200 milisegundos. El motivo de mi rendimiento más lento es que estoy ejecutando un programa de grabación de pantalla en segundo plano ahora mismo en está comiendo ciclos de CPU en la asignación de memoria disponible. ¿ Recuerdas la conferencia sobre cuerdas inmutables? Mostré que la clase de cadena es una clase inmutable, lo que significa que cualquier modificación a la cadena crea una nueva cadena. Esto hace que la clase de cadena se comporte como un tipo de valor, pesar de que en realidad es un tipo de referencia. Entonces, ¿qué pasa en la memoria durante el Luke cuando estoy sumando a la cuerda? Aquí en las pizarras están las primeras 3 iteraciones fuera del bucle. Se puede ver que se trata de un proceso súper ineficiente. Cada vez que tía, un personaje a la cuerda. Toda la cadena se copia a una nueva cadena en el montón. Mi abrigo está haciendo 10 miles de operaciones de copia de memoria detrás de bambalinas. Otra gran desventaja de este abrigo es que deja un rastro fuera de objetos de cuerda referenciados en el montón para 10,000 iteraciones. El código deja atrás 9999 objetos de cadena muerta. El recolector de basura va a tener mucho trabajo limpiando eso todo ahora. Contraste esto con un constructor de cuerdas. Un constructor de cadenas utiliza una matriz de caracteres fuera de cierta longitud predeterminada y simplemente escribe texto en la matriz. Entonces las 1 ª 3 iteraciones se veían así mucho mejor, ¿ no crees? Cada adición de cadena escribe un carácter en el tu A. Esto es mucho más eficiente, y cuando terminamos con el stream, simplemente de referenciado El constructor de cadenas en el recolector de basura solo necesita limpiar un objeto único, por lo que aquí está el principal quitarle esta conferencia. Si estás agregando cadenas juntas en tu código, siempre usa un constructor de cadenas. Trate de evitar agregar cadenas usando el operador plus. No hago mis mediciones anteriores realizando 10 mil adiciones. Pero, ¿cómo se apilla este constructor de cadenas cuando solo hago una o dos adiciones? Averigamos yo por lo que encuentra mis códigos así. Tengo dos bucles ahora. El primer look solo hace un número limitado. Ediciones de cuerda 2 a 19. El bucle externo repite este experimento 10,000 veces. Andi suma el tiempo total. En milisegundos, arroja el número de adiciones de 2 a 19 y por cada suma cuenta. Mostro el número total fuera de milisegundos para cadenas regulares en para el constructor de cadenas. Déjame ejecutar el programa. Aquí vamos allá hasta por cuatro adiciones. Las cadenas regulares son en realidad más rápidas que los constructores de cadenas por encima de cuatro adiciones. El constructor de cuerdas es más rápido. Aquí está la salida del programa, trazada como un gráfico de líneas. El renglón azul en el gráfico es el rendimiento fuera de las ediciones de cuerdas regulares. Se puede ver que las cadenas irregulares superan a los constructores de cadenas hasta cuatro ediciones. Eso son cinco o más adiciones. La clase de constructor de cadenas se vuelve más eficiente en cuanto más adiciones hagas, grandes sean los conductores de rendimiento de los constructores de cadenas se convierte en Ahora. El motivo de este comportamiento es que la clase de constructor de corriente tiene algo de sobrecarga en la configuración de su terreno de carácter interno, manteniendo la pista fuera de las alas de cuerda encendidas. Y es la capacidad de búfer interna en expandir este buffer cuando sea necesario. El cono que utiliza cadenas regulares justo antes de una secuencia fuera de las operaciones de copia de memoria para implementar las adiciones. Estos son en realidad más eficientes que el constructor de cadenas, pero sólo hasta un punto, y ese punto es exactamente cuatro ediciones. Entonces, ¿cómo debe hacer concatenación de cuerdas? Bueno, si tienes cuatro adiciones o menos, no hay nada de malo en usar variables de cadena regulares en el operador plus. Esto realmente te dará el mejor rendimiento si vas a agregar más de cuatro cadenas juntas o no sabes el número de adiciones de antemano utilizado para string Builder En cambio, una vez que obtienes hasta miles de descuento adiciones o, en este caso, 10 miles de ediciones, exactamente este constructor de cadenas es en realidad más de 240 veces más rápido que las adiciones de cadena regulares . Entonces en ese escenario, siempre use el constructor de cadenas 12. Consejos #3: usa la clase de lista correcta: si queremos almacenar una colección fuera de valores. Hay muchas posibilidades en el marco dot net. Contamos con las clases de cobranza en el sistema de colecciones de puntos. Nombre espacio. Contamos con colecciones genéricas en el sistema. Hace colecciones. Hace nombre genérico espacio Onda. Tenemos tipos regulares una carrera. Entonces, ¿qué enfoque es el más rápido? Vamos a averiguarlo. Déjame cambiarme a mi abrigo. Empezaré midiendo el rendimiento a menudo lista de array. Tengo un bucle aquí que agrega un millón de enteros a la lista de matrices. Aquí abajo están los segundos perdidos que además suma un millón de insurgentes. Pero aquí usa lista genérica fuera de tipo entero para almacenar los valores. Ejecutemos los códigos para comparar el rendimiento. Aquí vamos en los resultados. Lejos, los códigos de lista de matriz tardan 254 milisegundos, mientras que la lista genérica solo toma 42 milisegundos. El listado genérico es seis veces más rápido. Para entender el motivo de esto, echemos un vistazo a los diseños de memoria para Ray List. Después de tres adiciones, la pila y el montón se veían así. Ahora bien, si recuerdas la conferencia sobre boxeo y unboxing, mencioné que muchas colecciones en el sistema de puntos colecciones nombre espacio uso objetos, matrices para almacenamiento interno. La lista de matriz no es una excepción, por lo que cada elemento en la lista de matriz es una referencia a un entero en caja, que está en otro lugar del montón. On Ya hemos visto que el boxeo y el Unboxing introduce una actuación significante. Overheads a tu abrigo. Ahora compare esto con el diseño de memoria fuera de una lista genérica. Nuevamente, la lista está en el calor, pero ahora los inter jurados también se almacenan directamente dentro de los elementos de la lista. Y esto se debe a que una lista genérica fuera de tipo entero utiliza un array entero nativo para el almacenamiento interno en no una matriz de objetos. Esto elimina todos los objetos. Referencias de la lista elementos sobre también elimina la necesidad de boxeo. Por lo que la principal toma de distancia es siempre utilizar listas genéricas y clases de colección en Evitar las clases en el sistema de puntos colecciones espacio de nombres para códigos de misión crítica. Ahora probemos un traje Alternativa. Un array entero nativo pre inicializado a un millón de elementos. Buceo sobre cada elemento de matriz y configuro el valor uno por uno. Permítanme comparar este abrigo con los otros dos. Aquí vamos ahora. Esto es mucho más rápido. El array nativo solo tarda 11 milisegundos en ejecutarse. En tanto que la lista genética necesita 52 milisegundos, la matriz nativa es casi cinco veces más rápida que la lista genérica. El motivo de este excelente desempeño es que el lenguaje intermedio que compila el compilador de C sharp, también, tiene soporte nativo para una carrera. Echemos un vistazo a mis compilaciones frías. Voy a poner un punto de ruptura en, ejecutar el programa, luego cambiar a vista de desmontaje. Aquí vamos, y aquí está la asignación. Como puede ver, la referencia a la matriz se carga primero en la pila de evaluación. Entonces la matriz se desplaza en el elemento en el que queremos escribir se carga en la pila en . Por último, el valor que se tiene que establecer en los elementos se agrega a la pila. Y luego viene una instrucción Set Elements, que escribe el valor en los elementos de matriz especificados. Por lo que tenemos una instrucción dedicada llamada set elements para escribir valores en una carrera. Nadie del código que usa una matriz es rápido. Ahora. Podrías estar hundiendo que estoy engañando. Debido a que la lista de matriz y las clases de lista genéricas utilizadas en matriz interna en continuamente redimensionar esta matriz cuando se desborda, mientras que mi matriz entrevistador ya estaba pre-inicializada a un millón de elementos. Por lo que nunca se desborda. Entonces déjame modificar mis códigos para eliminar esa sobrecarga. Iré a la línea aquí donde se inicializa la lista de matrices. Pongo en una capacidad por defecto un millón de elementos. Entonces bajaré a esta línea en adelante. Hago lo mismo para la lista genérica. Ahora déjame volver a ejecutar el código. Ya ves, la lista genérica ahora tarda sólo 31 milisegundos en ejecutarse, que es un 40% más rápido que la ejecución anterior. El listado de arreglos toma 187 milisegundos, lo que es una mejora de sólo 26%. El array nativo tarda 12 minutos. segundo encendido sigue siendo la opción más rápida, 2.5 veces más rápida que la lista genérica. El construido en apoyo para Boinas en el lenguaje intermedio asegura que siempre superarán a las clases de cobranza. El listado genérico y la lista de matriz simplemente no pueden competir con ese rol Performance. Aquí hay una gráfica Con todos los resultados de rendimiento, la lista de array pasa la mayor parte de su tiempo boxeo entierró tus datos pre dimensionar los más frailes a través del número correcto de elementos aumenta el rendimiento en un 40% pero todavía no es un muy buenos resultados. El listado genérico es mucho más rápido porque puede almacenar la introducción directamente en los elementos de lista en el montón sin necesidad de una referencia de cuadro extra y pre dimensionar la lista al número correcto. Off elements aumenta el rendimiento en 26%. El array nativo sigue siendo la opción más rápida. Incluso con listas precisas, la matriz sigue siendo 2.5 veces más rápida que la lista genética. Entonces, ¿cuándo deberías usar una carrera? Bueno, si tienes códigos de misión crítica en el número off elementos es saber de antemano usando array. Si tienes códigos de misión crítica en el número off elementos no se conoce de antemano, utiliza una lista genérica. Evita las clases en colecciones de puntos del sistema. Utilizan el boxeo en nuestro sustituido por las clases genéricas en sistema Las colecciones DOT no genéricas 13. Consejos #4: usa el tipo de matriz correcto: En la conferencia anterior, miramos las colecciones genéricas y no genéricas y las comparamos con las criadas nativas. Resultó que las matrices nativas son las más rápidas, pero las colecciones genéricas tienen la ventaja de que crecen automáticamente que los elementos Portmore en ellas. Entonces si el número fuera de los elementos se conoce de antemano y array es la opción más rápida, pero el marco de punto net en realidad admite tres tipos de una carrera. El primer tipo es el array unidimensional que hemos visto en la conferencia anterior. Cualquier cosa declarada con los corchetes de rayos corchetes en C Sharp es una matriz unidimensional, por ejemplo, la matriz entrevistadora que se muestra aquí. Declaras la matriz así, Andi, accedes a elementos como este. El segundo tipo es un arreglo multidimensional. Por ejemplo, una matriz bidimensional se declara así, y se accede a elementos como este. El tercer tipo es un array de chaquetas. Esto es simplemente una matriz fuera de una carrera. Declaras una chaqueta, una boina como esta y luego creas una nueva instancia de matriz para cada elementos de nivel superior como este , luego accedes y elementos como este. Se llama matriz de chaquetas, porque cada elemento de nivel superior puede tener un número de diferencia fuera de los elementos de subnivel. Una matriz de dos por dos puede tener un gato lado derecho como este. Entonces, ¿cómo se comparan estas matrices? Bueno, vayamos a Xamarin Studio y averigamos que he escrito un programa que inicializar es tres. Elevar cada uno conteniendo un millón de elementos. Aquí está la matriz unidimensional, con 1000 veces 1000 elementos en este bucle signos de valor a cada elemento. Y aquí está la matriz bidimensional, ahora inicializada a los miles por 1000 elementos. Tengo dos bucles anidados para pasar por cada fila y columna, y asigno valor a cada elemento de matriz. El tercer mensaje utiliza array chaquetas. El inconveniente de las matrices de chaqueta es que hay que inicializar toda la matriz externa con instancias de array. Ese código está aquí. Vamos a averiguar qué matriz es la más rápida. Listo. Aquí vamos. Esto probablemente no fue una sorpresa. El array unidimensional es el más rápido, con 12 milisegundos. Después viene el array de chaquetas con 16 milisegundos y finalmente el array bidimensional con 24 milisegundos. Ahora probablemente recordarás de la conferencia anterior que el lenguaje intermedio tiene soporte nativo para el borrado unidimensional Esta es la razón por la que la matriz unidimensional es más rápida en la matriz dentada viene en segundo lugar. Una matriz dentada es simplemente una matriz unidimensional anidada dentro de otra matriz unidimensional . Por lo que aún obtienes los beneficios de velocidad del soporte de lenguaje intermedio integrado. Quizás sorprendentemente, la matriz bidimensional es la más lenta, y esto se debe a que una matriz bidimensional se complació. Net es solo una clase sin ningún soporte especial en tiempo de ejecución. Aquí, déjame mostrarte mis compilaciones. Frío. Si configuro un punto de ruptura, vuelva a ejecutar el programa al cambiar a vista de desmontaje ons. Busca aquí la línea correcta. Aquí está la matriz unidimensional. Se puede ver la instrucción de conjuntos familiares elementos que escribe el valor en el Eres un elemento. Y aquí está el jittery JAG primero una instrucción de elementos bajos a cargas, la referencia a la matriz interna y luego una instrucción de elementos establecidos a derechos un valor en esa matriz interna. Pero ahora mira la matriz bidimensional. Es simplemente una llamada a un conjunto estático. Métodos fuera del método de su Clase A uno llaman para cada acceso de elementos, además de cualquier implementación que esté dentro de ese método set, eso son muchos más códigos para ejecutarlos. Las matrices unidimensionales y dentadas. Ahora acabas de ver que una matriz unidimensional es más rápida que una matriz bidimensional, lo que podemos acelerar una matriz bidimensional mediante el uso de una técnica llamada aplanamiento de matriz. Echa un vistazo a este tres por tres ya fuera enteros. Eso es un total de nueve elementos que puedo exponer así. Tengo código de color cada fila de elementos. Ahora también puedo empacar todos estos datos en una matriz unidimensional como esta puedo acceder y elementos dados la fila y columna así. Entonces probémoslo en código. Tengo un programa aquí que configura una matriz de enteros 1000 por 1000 y luego pierdo a través de todos los elementos accediendo a ellos uno por uno. Aquí está el mismo abrigo, pero con una matriz unidimensional con un millón de elementos. Todavía tengo los dos bucles anidados para acceder a All rose en todas las columnas, pero ahora utilicé la fórmula de traducción para aplanar la fila y la columna en un índice unidimensional . ¿ Qué abrigos Crees que es más rápido? Es difícil de decir. En realidad, sabemos que la matriz unidimensional será más rápida. Pero ahora también tenemos los gastos generales fuera haciendo la multiplicación extra para traducir la fila y la columna en un índice aplanado. ¿ La multiplicación extra compensará los gastos generales de la matriz bidimensional? Vamos a averiguarlo. Ahora estoy ejecutando el programa. Yo estaba aquí estamos. El array unidimensional sigue siendo la opción más rápida, con 15 milisegundos en comparación con el array bidimensional. Con 22 milisegundos, la matriz aplanada es 1.5 veces más rápida. Aquí hay un gráfico con todos los resultados de rendimiento. El array bidimensional es dos veces más lento que el array unidimensional. Incluso en la prueba de aplanamiento, cuando el array unidimensional tenía los gastos generales extra fuera teniendo que hacer una multiplicación, sigue siendo 1.5 veces más lento. Esto sugiere que aplanar matrices bidimensionales es una buena idea. Quizás sorprendentemente, la matriz Jagged también tiene un buen rendimiento . Es sólo 1.3 veces más lento que la matriz unidimensional activada cuando se compara con los resultados de aplanamiento, hay casi igual a 15 milisegundos para los aplanados matriz una dimensional en 16 milisegundos. Para la matriz dentada, la diferencia en rendimiento es de solo un milisegundos, que es del 6%. Entonces, ¿cómo debes usar una carrera si solo tienes una dimensión fuera del día za, usa una dimensión borrada para el mejor rendimiento. Si tiene dos o más dimensiones de datos, considere aplanar la matriz. Si esto no es posible, considere usar una matriz de chaquetas. Si no hay otra opción, utilice una matriz multidimensional. 14. Consejos #5: evita la salida de excepciones: en esta conferencia. Yo quiero echar un vistazo al uso responsable fuera de las excepciones. Entonces, ¿qué son las excepciones? El marco Net utiliza excepciones para manejar errores. Entonces así es como funciona eso cuando algo sale mal en un bloque de códigos que los códigos pueden arrojar una excepción. El uso de las sentencias throw los métodos abortan inmediatamente en la pila se desenrolla, lo que significa que el marco sigue ejecutando sentencias de retorno implícitas, saltando fuera de las llamadas de método anidado en ir más alto y más arriba de la pila de llamadas hasta que alcanza abrigo con un try catch block. El marco salta al primer bloque catch que coincide con la excepción del trono y comienza a ejecutar el código Ahí. Se puede capturar la excepción poniendo un parámetro en la expresión catch, para que seguro suene como muchos gastos generales, ¿ no? ¿ Qué tan grande crees que será la sobrecarga de rendimiento para lanzar y atrapar una excepción? Bueno, vamos a averiguarlo. He escrito un programa que repite operación muy simple en este caso, increment ing en entero un millón de veces. Aquí están los primeros métodos de medición que simplemente incrementa y variable entera que fui. Aquí está el segundo método de medición, los mismos códigos, pero después de incrementar la variable el código lanza sobre la adhesión de operación inválida. Ahora las declaraciones de lanzamiento están dentro de un look try catch, por lo que no hay necesidad de que el framework dot net comience a desenrollar la pila en busca de un bloque try catch. Ya estamos dentro de un try catch book, por lo que la ejecución salta directamente a la declaración catch, que en este caso, no hace absolutamente nada. Por lo que este código medirá los realiza gastos generales de un solo tiro y captura. No hay gastos adicionales porque la pila necesita ser desenrollada. Tampoco hay gastos generales porque fuera de los códigos de manejo de excepciones porque en este caso, el bloque de captura está anti listo. Aquí vamos. Yo estoy ejecutando el programa. ¿ Esperabas que los gastos generales fuera lanzar una excepción es incremento masivo ing una variable entera un 1,000,000 veces toma solo seis milisegundos, pero lanzar en la excepción dos millones de veces toma un adicional de 6.9 segundos, no tan solo segundos. El código, con manejo de excepciones, es un asombroso 1150 veces más lento. Esto lleva al principal despegue este apartado. Nunca use excepciones en los códigos de misión crítica. Suficiente justa. Simplemente evitaré usar las declaraciones de tiro en mi abrigo, y estamos bien preparados? Bueno, no. También necesito asegurarme de que las bibliotecas en clases base que uso también no arrojen excepciones. El hecho es que muchos métodos de clase usaban excepciones para señalar una condición no crítica a la persona que llama. Si quiero evitar esas excepciones, no puedo llamar a estos métodos y directamente. Pero tendré que revisar mi incluir ater a fondo en busca de errores. Echemos un vistazo al sencillo caso de uso. Voy a ejecutar el programa que convierte cadenas para introducir. Echa un vistazo a este código. Tengo un mensaje aquí llamado Preparar Lista. Utiliza un constructor de cadenas para ensamblar un número de cinco dígitos usando un generador de números aleatorios. Cuando la cadena está lista, se almacena en esta lista genérica. El código se repite un 1,000,000 veces, así que eventualmente tendré un 1,000,000 cadenas para analizar. Aquí abajo están los métodos de medición. El 1er 1 bucla a través de todas las 1,000,000 cuerdas en usos. El método de parte entera para convertir el dedo del pie del arroyo un número. Ahora bien, si algo sale mal durante el análisis de los métodos de piezas arroja una antigua excepción. Capto la excepción aquí en simplemente suprimir el error. El segundero mide, los métodos hace lo mismo. Recorre todas las cuerdas de Parsons una por una. Pero ahora uso el try. Analizar métodos en lugar del pársón no es intentar. Partes intentarán analizar la corriente en simplemente no hacer nada. Cuando falla el análisis, nunca lanzará una excepción, así que no necesito probar catch book. Aquí. Se puede ver la diferencia en las pláticas de pecado. Pruebe Pars devuelve valor booleano que indica si el parson fue exitoso, también tiene un parámetro de salidas para almacenar el resultado. Compare eso con los métodos de análisis, que simplemente devuelve los resultados de paso directamente y no tiene ningún parámetro o argumentos para indicar si el análisis fue exitoso o no. De acuerdo, vuelta a los métodos de lista de preparación. Echa un vistazo a la costa. ¿ Ves algo extraño al final fuera de mi matriz de caracteres? Tengo una letra extra X. El generador de números aleatorios aquí genera un número entre cero y 10 por lo que 10% de descuento en el tiempo que agregará la letra X al arroyo en lugar de un dígito válido. Si pongo un punto de ruptura justo más allá del método, llame ejecute el programa en, luego inspeccione el contenido fuera de la lista genérica. Entonces ves que un par de números no son válidos porque contienen en X en lugar de un dígito. El análisis y los métodos de piezas de prueba fallarán en estos números sólo en las partes. Los métodos lanzarán una excepción cuando esto suceda. Entonces en base a lo que ya sabemos de las excepciones en que son realmente lentas, esperamos que los métodos try parse funcionen mucho mejor que el método de piezas. ¿ Qué tan grande crees que será la diferencia? Vamos a averiguarlo. Voy a ejecutar el programa, una medida la diferencia en rendimiento. Aquí vamos. Y aquí están los resultados. El Tri Parse Methods tarda 618 milisegundos en Roma. El método de piezas toma 3727 milisegundos, por lo que el análisis es seis veces más lento que el try pars debido al manejo de excepciones extra. Por lo que hemos visto que las conversiones de tiempo de datos también pueden ser problemáticas. Abrigo de misión crítica. Debido a que muchos métodos de conversión arrojan excepciones cuando los datos de entrada no son válidos,¿ válidos, existen otras situaciones comunes en las que se lanzan excepciones? Sí, eso te voy a mostrar una más. Entonces aquí hay otro programa, y en este programa estoy haciendo lo contrario. Fuera del programa más grande. Estoy convirtiendo enteros de nuevo dos cadenas. Tengo un mensaje de lista preparado aquí. Guardias. Ahora llena un individualista genérico con un millón de enteros aleatorios. Andi Abajo aquí están los primeros métodos de medición, que recorre cada entero de la lista, y para cada número, utiliza una tabla de búsqueda para convertir ese número en una cadena. Si miras aquí arriba, verás que he pre definido en inter tu diccionario de cadenas para actuar como buscar tabla indexando la tabla buscar con un entero. Puedo encontrar rápidamente la cuerda que corresponde a esa entrevista. Por lo que volver a los códigos de medición. Utilizo la tabla de búsqueda para encontrar la cadena y almacenarla en la variable s Si algo sale mal, esta declaración catch va a captar la excepción al suprimir la flecha. Ahora echemos un vistazo a estos segundos métodos de medición. Hace exactamente lo mismo, pero tiene un cheque extra aquí. Probar si el entero está en realidad en el diccionario antes de usarlo para buscar el flujo correspondiente. Esto, excepto que la comprobación evita una excepción. Y así no necesito intentarlo. Bloque de efectivo. De acuerdo, vuelta al mensaje de lista de preparación. ¿ Ves el in validador? Es muy sencillo. Tengo 10 entradas en el diccionario para los dígitos de 0 a 9, pero el generador de números aleatorios genera números entre cero en 10. Por lo que en 10% de descuento en los casos, tendré el número 10 en la lista de la mesa de búsqueda no tiene entrada para ese número. Si pongo el punto de ruptura justo más allá del médico, ejecuto el programa y luego inspecciono el contenido fuera de la lista genérica, entonces se puede ver que ocasionalmente tengo un 10 en la lista. El diccionario buscar hacia arriba, fallará para este número y sólo la medida. A los métodos lanzarán una excepción cuando esto suceda. Entonces en base a lo que sabemos acerca de las excepciones sobre la sobrecarga de rendimiento de las excepciones, esperamos que la medida A sea más lenta que la Medida B. Así que vamos a verificar que voy a ejecutar el programa y medir la diferencia en el rendimiento . Aquí vamos y aquí están los resultados medir una toma 1180 milisegundos para ejecutar la medida ser solo toma 167 milisegundos, por lo que la Medida A es siete veces más lenta que la medida sea debido al manejo de excepciones extra . Aquí están todos los resultados de medición Combina en un solo ref aquí abajo hay partes y prueba pars con el método de piezas tomando 3727 milisegundos en try pars solo necesitando 618 milisegundos porque no necesita lanzar una excepción si los datos de importación son inválido, esto es una caída en el tiempo de ejecución apagado superior al 80%. Entonces la prueba de segundos con el diccionario look ups realizando una mirada hacia arriba en el manejo excepciones toma 1180 milisegundos. Pero si comprobamos primero la clave y luego realizamos el look up, evitamos excepciones todas juntas. En la carrera, tiempo baja nuevamente a solo 167 milisegundos, una caída en el tiempo de ejecución apagado más del 80%. Entonces, ¿cuáles son las mejores prácticas para usar excepciones? Bueno, si usas excepciones en un bucle de misión crítica, entonces solo las usaste para indicar una condición fatal que requiere que te pongas a bordo del bucle por completo. No use excepciones para condiciones no fatales que maneja simplemente pasando a la siguiente iteración de bucle. No pongas. Prueba los bloques de captura en código profundamente anidado o en funciones AP I de bajo nivel porque ralentizarán tus abrigos. Ponga el manejo de excepciones lo más cerca posible del programa principal. Nunca use una captura genérica. Declaraciones de excepción, que almacena en cachés todas las excepciones porque también capturará condiciones no fatales el. No será inmediatamente obvio por qué tu código es lento Si estás escribiendo un A P. No uso excepciones para condiciones de límite no críticas como convertir datos no válidos o fallar una operación de búsqueda. Considera el patrón try parse para eso con un valor de retorno booleano que indica éxito y parámetro out para devolver datos. Hagas lo que hagas, Nunca uses excepciones para controlar el flujo de tu programa. 15. Consejos #6: utiliza para en vez de foreach: esta conferencia va a ser todo sobre la optimización de bucles. Un consejo común que a menudo escuchas sobre acelerar bucles es que se supone que debes usar un cuatro enunciados en lugar de que cada uno mire por encima de la colección. Pero es esto cierto en, Y si es así, ¿qué tan grande es la diferencia en rendimiento entre los dos? Empecemos por mirar a la mecánica fuera de Z cuatro y por cada enunciado, comenzaré con cuatro. Cuando se utiliza un bucle for para acceder a elementos de una colección, generalmente se inicia con un bucle que cuenta desde cero hasta el número off elementos en la colección Mentes uno. Entonces usamos en índice de expresión para acceder a cada elemento. A su vez, un Luke de cuatro solo es posible si tienes acceso directo a algún elemento de la colección, generalmente a través de los corchetes. En meditación extra en C sharp, la matriz enumera lista genérica en clases de array. Todo soporta indexación. Otra forma de acceder a los elementos de una colección es mediante el uso de on in numerador detrás bambalinas. El para cada enunciados comienza por crear un nuevo objeto en numerador en otras maquinillas de afeitar tienen sólo tres miembros. Un campo actual que contiene el valor del elemento de colección actual un movimiento siguiente mensaje para pasar al siguiente elemento. Andi reset métodos para saltar de nuevo al inicio de la colección. Entonces, por lo tanto, cada sentencia configura un bucle que sigue llamando movimiento siguiente hasta que llegue al final de la colección. Durante cada iteración de bucle el campo actual contiene al valor de elementos actuales. Entonces, ¿qué declaración es más rápida? Es difícil de decir. En realidad, los cuatro estadistas requieren de un índice, por lo que en el costo de descuento, poder acceder directamente a cualquier elemento puede ser alto. Pero por otras manos, Ford cada uno necesita configurar en objeto aireador inem en, luego llamar al movimiento siguiente mensaje en cada bucle, it oration. Entonces todo depende cuál es más rápido, el indexador o el movimiento. Siguiente métodos Aquí están los pros y los contras de cada método. El enunciado cuatro es potencialmente el más rápido debido a su simplicidad, pero requiere una colección con un indexador. En las colecciones no se sabe de antemano, en qué orden se accederá a los elementos a través del indexador, lo que todos los valores deberán cargarse en la memoria primero, cada uno es más complejo porque utiliza un enorme cráter detrás de bambalinas, y requiere una llamada al movimiento siguiente métodos para avanzar al siguiente elemento. Pero la ventaja off en operación es que funciona en cualquier colección. Otra ventaja es que el valor actual se calcula bajo demanda, por lo que toda la colecta no tiene que estar en memoria para ser inaugurada. Asesoramiento en Internet alrededor de cuatro y para cada uno es mixto, algunos dicen que siempre se utiliza para. Pero otros dicen que las mejoras de rendimiento no valen la pena. Entonces permítanme mostrarles un programa que mide el desempeño de ambas declaraciones para una variedad fuera de colecciones. He escrito el programa que configura tres colecciones de 10 millones de enteros una lista de array , una entrevista genérica lanzada en una matriz de enteros regulares. En este método se inicializan las listas. Aquí. Preparar listas. El mensaje genera 10 millones de números aleatorios entre cero y 255 en los anuncios. Ellos dedo del pie las tres listas, luego vienen a métodos de medición. Aquí abajo, hay seis de ellos. Empezaré con la primera medida 1. A uno se suelta a través de los 10 millones de elementos de la lista de matriz con un bucle for simple y utiliza el indexador incorporado de la clase iraníes para acceder a los elementos. Ahora el siguiente mensaje mide A para también bucles a través de todos los elementos fuera de la lista de matriz. Pero ahora, usando a para cada enunciados en lugar de las cuatro sentencias, por lo que detrás de las escenas para cada uno creará on en numerador objetos que transita secuencialmente a través de cada elemento de la colección. Los dos métodos siguientes son medida Ser una tías Medida B dos. Hacen exactamente lo mismo, pero con el genérico entierró su lista en lugar de la lista de matriz. Y por último, tengo los métodos Medida C uno y Medida C dos. Nuevamente, miraron a través de los 10 millones de enteros, pero ahora usan el array entero. Por lo que ves las tres clases de colección apoya tanto la indexación en admiración, que será más rápida. Averigamos que estoy ejecutando el programa ahora, así que esperemos los resultados. Y aquí están los resultados. Usar cuatro array caído toma 98º milisegundos usando para cada Alan Array toma cientos y tres milisegundos, una diminuta diferencia. Pero usar cuatro y para cada uno en la lista genética, toma respectivamente 300 ochos en 501 milisegundos, y obtenemos la mayor diferencia en rendimiento al usar cuatro y para cada uno en una lista de matriz , respectivamente. 302 en 872 milisegundos. ¿ Ves eso para un aumento? La diferencia es muy pequeña en optimizar A para cada dos o cuatro probablemente no valga la pena el esfuerzo. Pero para la lista genérica y la lista de array, la diferencia es bastante grande. ¿ Por qué es eso? Echemos un vistazo al frío intermedio. Empezaré con el método Measure C one que utiliza un bucle for simple para acceder a cada elemento en una matriz entera. Entonces aquí está el bucle for. Estas tres instrucciones de lenguaje intermedio establecen la variable en 20 Entonces saltamos a ubicación una F on para comparar la variable I con el valor de 10 millones. Si tengo menos de 10 millones, seguimos aquí en Ejecutar el cuerpo de bucle. Se puede ver la instrucción familiar del elemento de carga aquí para acceder en elementos de matriz. Por último, los códigos continúan aquí, donde no somos uno a la variable por cheque de nuevo. Si ahora son menos de 10 millones, veamos los mismos abrigos en el método Medida C dos. Es casi el mismo cierre de la variable de índice a cero, saltando a la ubicación 24 comparando el índice con la longitud fuera de la matriz. Andi continuando si es menos. Pero mira este pedacito aquí justo antes de ejecutar el nuevo cuerpo. Tenemos cuatro instrucciones. Ubicación de carga para ubicación de carga. Tres elementos de carga en ubicación de la tienda. Uno. Estas cuatro instrucciones. Recuperar los elementos de la matriz actual y almacenarlo en la ubicación uno, que corresponde a la variable I. Así que la diferencia entre un cuatro y A para cada enunciado para matrices regulares es sólo estos cuatro lenguajes intermedios instrucciones sobre esto es por lo que las medidas diferencia de rendimiento entre los dos es tan pequeña. El compilador en realidad no crea en el numerador objetos usando corrientes y se mueve a continuación. En cambio, emite un look cuatro ligeramente modificado para implementar la enumeración. Ahora veamos las listas genéticas. En la medida. Ser uno en la Medida B dos métodos. El acto, ser uno métodos, es muy sencillo. En primer lugar, obtenemos el familiar conjunto de instrucciones para el bucle de cuatro aquí. Después, al acceder a los elementos de lista, los códigos utilizan una llamada virtual a un indexador. Llamadas obtienen artículo. El valor de retorno es un entero que se almacena en la ubicación a. Pero la Medida B dos es muy diferente. El para cada sentencias comienza llamando a los métodos get in numerador en los objetos de lista . El listado en numerador es en realidad una estructura y no una clase on. Se almacena en ubicación a los códigos, luego llama. Muévete a continuación en el operador en y continúa si el resultado es cierto. A continuación, obtiene el valor de los elementos actuales llamando a la propiedad, obtiene corrientes y almacena los resultados en la ubicación uno. Después se ejecuta el cuerpo de bucle, y luego estamos de vuelta en movimiento a continuación. Este abrigo es mucho más complejo que simplemente acceder a los elementos de lista a través del índice. Enhanced , corre más lento. Ahora veamos la lista de matriz en los métodos. Medir una medida de £1 A. Para no hay nada especial en Measure A one, un regular for loop en una llamada virtual al indexador de artículos obtiene. Pero mira este pedacito aquí. El get item inyection devuelve y objetos, no un entero. Entonces antes de poder almacenar los resultados, tiene que ser unbox con esta instrucción unbox aquí, y ya hemos aprendido que el boxeo y el unboxing introduce un rendimiento importante en tu costa. Ahora aquí se mide. A a este abrigo se ve muy similar al culto de lista genética en la Medida B dos. Pero mira las diferencias aquí Primero. El listado de matriz en numerador es una clase sobre no atado, lo que significa que el cool para conseguir actual es una llamada virtual en lugar de una llamada regular. Esto podría parecer un detalle trivial, pero la instrucción colvert es, dehecho, hecho, más lenta que la instrucción de llamada. Pero en segundo lugar, obtener retornos actuales con objetos y no un entero. Por lo que el resultado necesita ser desbox nuevamente antes de que pueda almacenarse, lo cual sucede aquí con la instrucción Unbox. Entonces aquí hay una importancia. Llevar ningún genérico en. Los operadores siempre devuelven el valor actual como objeto. Entonces si los usas dedo del pie en operar tipos de sobrevalor, se descajarán en segundo plano siempre utilizados enumeradores genéricos, si es posible. Aquí están todos los resultados de medición combinados en una gráfica usando £4 para cada uno en una matriz toma, respectivamente, 98 menos de cientos y tres milisegundos usando cuatro y para cada uno en una lista genética toma respectivamente 308 y 501 milisegundos en usar cuatro y para cada uno en una lista de matriz toma, respectivamente, 302 en 872 milisegundos. Hemos visto que hay muy poca diferencia entre un cuatro bajo para cada uno cuando en operar una matriz regular. El motivo de esto es que el compilador optimiza array y admiración emitiendo un bucle cuatro ligeramente modificado. En lugar de pasar por la molestia fuera, creando en el operador objetos con listas genéricas, vemos una clara diferencia entre cuatro. Y para cada uno, el numerador es instruct, que es un tipo de valor, y así puede almacenar tipos de valor como enteros de manera eficiente y sin embargo en admiración, sigue siendo 1.6 veces más lento que usar un bucle for. Entonces aquí tiene perfecto sentido del dedo del pie optimizar un para cada bucle reescribiendo su en un bucle for . Y por último, al mirar listas de rayos, hemos visto que hay mucho de unboxing pasando detrás de bambalinas. El indexador regresa. Un objeto que necesita ser lanzado a un entero en el operador in es una clase y no destruir en. Devuelve el valor actual como un objeto, que nuevamente necesita ser lanzado a un entero en operación en una lista de matriz tiene una penalización enorme . Se trata de dos puntos ocho veces más lento que usar un bucle for. Entonces así es como eliges entre cuatro y para cada uno. Si estás usando una matriz, no te molestes en reescribir un para cada uno en un cuatro. La diferencia en rendimiento simplemente no vale la pena. Al usar una lista genérica, usar un cuatro sentencias es 1.6 veces más rápido que usar a para cada sentencia, lo que reescribir para cada una en un bucle for definitivamente vale la pena. Y para las listas de arreglos, las mejoras son aún mayores. Una sentencia de cuatro en una lista de arreglos es 2.8 veces más rápido. Pero si piensas en todo el boxeo y el unboxing que está pasando en segundo plano, es posible que realmente quieras considerar reescribir la lista de array para que use una lista genérica en su lugar. Y por último, si utilizas para cada uno y estás en operar una colección fuera de los tiempos de valor, siempre asegúrate de que estás usando el genérico Anoma Reiter, que está en la E una interfaz de té de hierbas nominal sobre no el no genérico en numerador, que está en la interfaz E innumerable en. El motivo de esto es que el no genérico en operador siempre devolverá el valor actual como objeto, por lo que tu abrigo compilado contendrá muchas instrucciones fuera de caja y desbox y usando un genérico en memorias que evitará esto. 16. ¿Cómo funciona el coleccionista de basura?: esta conferencia es por peticiones fuera chupar. Es gracioso ¿quién me pidió que buscara la recolección de basura? Gracias por la petición, mamones. Espero que disfruten de esta conferencia, y también espero pronunciar tu nombre correctamente. Si alguien más tiene alguna petición especial sobre cuál es el tema que le gustaría que dirija, envíeme un mensaje y lo trabajaré en mi hoja de ruta o futuras lecciones. Si recuerdas en la segunda conferencia fuera de este curso, la de la memoria del montón en la sección de fundamentos, vimos lo que sucede cuando las variables de tipo de referencia salen del ámbito. Las variables que existe en la pila se destruyen en los objetos correspondientes en el montón. R D Los objetos referenciados de referenciados siguen existiendo y no se destruyen inmediatamente. Mencioné brevemente que un proceso separado llamado el recolector de basura limpia periódicamente estos objetos. Entonces en esta conferencia, vamos a echar un vistazo más de cerca al recolector de basura. ¿ Qué es el recolector de basura y cómo funciona? Empecemos con un programa muy sencillo. Este programa sólo tiene un mensaje con un array de objetos variables locales. El array se inicializa con cinco objetos en estos cinco objetos también residen en el montón adyacente a la matriz misma. Ahora, para hacer las cosas más interesantes, eliminemos los elementos de matriz dos y tres sentados. Ahí array elementos para conocer los objetos correspondientes. Los números dos y tres siguen existiendo en el calor, pero ahora están de referenciados. No hay referencias a estos objetos desde ninguna parte del abrigo. ¿ Qué pasa cuando el recolector de basura patea en la puerta? Net. recolector de basura es un colector de marcas y barrido, que deja. Hay dos etapas distintas fuera de la basura recolectando una etapa de marca en una etapa de barrido durante la etapa de marca. El recolector de basura marca todos los objetos de vida en el así en este ejemplo, que sería la matriz en sí y los objetos 01 en cuatro. los objetos dos y tres omitenlos objetos dos y tresporque no hay referencias a estos objetos desde cualquier parte del abrigo. A continuación viene la etapa de barrido todos los objetos que no se han marcado en la etapa anterior R D referencia on, y en esta etapa son de asignados desde el montón. Entonces en este ejemplo, los objetos dos y tres no han sido marcados, y así son los asignados en. Esto deja una especie de agujero en el montón. El recolector de basura de red para perros realiza un paso adicional después del barrido, que se llama compactos. En la etapa compacta, se eliminan todos los agujeros en el montón, lo que en este ejemplo, el objeto cuatro se mueve hacia arriba para llenar el agujero Ese objeto a dejar atrás la marca y barrer recolector de basura es muy bueno en ubicar. Todos y cada uno de los objetos referenciados en el montón en eliminarlo. Pero también tiene un gran inconveniente. Es muy lento. Durante la etapa de marca, el recolector de basura tiene que inspeccionar cada objeto en la memoria para determinar si es vida o referenciado. Si hay muchos miles de objetos fuera en el montón, su programa realmente se congelará por un tiempo ya que el recolector de basura está inspeccionando todos y cada uno de los objetos. El proceso también es muy ineficiente porque los objetos de larga vida en el montón son revisados y rerevisados, soportando cada ciclo porque podrían ser d referenciados en cualquier momento. Por lo que un objeto vivo muy largo podría ser revisado cientos de veces si aún está vivo. A la solución a este problema se le llama recolector de basura generacional, el recolector de basura de redes de puntos es generacional, y tiene tres generaciones, que se pueden visualizar como tres montones separados. Todas las nuevas asignaciones van al primer montón generacional llamado Generación Cero. Entonces si volvemos a visitar el programa de prueba con la matriz de cinco elementos con elementos dos y tres, resolver no. Entonces los diseños de memoria se verían así. Todo es igual que antes, pero ahora todos los objetos están residiendo en generación cero generaciones. Se llega uno a nuestro anti. El primer ciclo de recolección hace una marca y barrido en todos los objetos que sobrevivieron al movimiento de barrido a Generación uno. Entonces, después de un ciclo, el diseño de la memoria se parece a esta matriz Z. Los objetos Andi 01 y cuatro han sobrevivido al barrido y ahora están en la generación uno. Ahora imagina que el programa continúa en este punto. Pone un nuevo objeto cinco en elementos de matriz a todas las nuevas asignaciones. Entra en una generación cero para que los diseños de memoria se vean así. Como ven, esta es una situación interesante. El array recita en la generación uno, pero sus elementos son en generaciones cero y uno. Esto es perfectamente válido. Ahora. El recolector de basura vuelve a patear por un segundo ciclo. Todos los objetos de generación uno pasan a generación a en el nuevo objeto en generación cero se mueve a generación uno si el programa continúa y pone un nuevo objeto seis en un raro elementos tres, volvería a entrar en generación cero. Ahora tenemos una matriz en generación para referirnos a objetos en la generación 01 como a otra prueba de la violencia. Por lo que podría estar preguntándose a estas alturas ¿por qué está pasando todo esto? ¿ Por qué han tenido estas tres generaciones? Bueno, la respuesta es muy simple. Las generaciones ayudan a limitar el número de objetos fuera en la generación cero. Cada ciclo de colección borra completamente la generación cero de todos los objetos. Por lo que en el siguiente ciclo, el recolector de basura sólo tiene que inspeccionar nuevos objetos que se crearon después del último ciclo. Por supuesto, el problema no va a desaparecer. El recolector de basura simplemente movió los objetos a otro lugar. Pero aquí está la generación clave una en cuanto a se recogen con muy poca frecuencia. El recolector de basura asume que cualquier cosa que llegue a generación debe ser un objeto de larga vida que no necesita ser revisado muy a menudo, por lo que esto resuelve dos problemas. En primer lugar, reduce el número fuera de objetos en generación cero, por lo que el recolector de basura tiene menos trabajo por hacer segundos, objetos de larga vida que sobreviven en generación a no me revisan muy a menudo, que es exactamente lo que queremos. El recolector de basura generacional es un hermoso algoritmo de alto rendimiento, pero tiene un inconveniente importante. Es ineficiente como procesar objetos grandes, de larga vida. Considerar un objetos grandes y de larga salida se asignarán en generación cero. Sobrevive al primer ciclo, y el calor se compacta, lo que potencialmente mueve el objeto en la memoria. Después pasa a la generación uno. Se compacta en movimientos a generación a todos. En total, se trata de dos compactación y dos movimientos, Así que un total fuera cuatro operaciones de copia de memoria para un solo objeto antes de que llegue en generación a en el recolector de basura lo ignora por un tiempo. Si el objeto es muy grande, estas cuatro operaciones de copia por objeto pueden introducir unos gastos generales de rendimiento de significación. Entonces la solución a este problema es tener dos montones separados, uno para objetos pequeños en uno. Para objetos grandes, el diseño se ve algo así. Redes de interior que son demasiado caderas. El objeto pequeño. Él, que trabaja con las tres generaciones que discutimos antes, y la punta de cultura grande, lo especial del montón de objetos grandes es que no utiliza generaciones. De hecho, sólo tiene una sola generación, cual se sincroniza con la generación a off del pequeño objetivo. Por lo que cuando el recolector de basura eran procesos generación para salir de lo pequeño de tu equipo, también corre a través de todo el objetivo grande. Otro dato interesante sobre el montón de objetos grande es que no compacta el durante el ciclo de barrido. Simplemente fusiona bloques de memoria libres juntos, pero no hace ninguna compactación para optimizar la cantidad total de espacio libre. Te podría estar preguntando ¿qué determina si un objeto es pequeño o grande? Bueno, el umbral de tamaño está en 85 kilobytes. Cualquier objeto a 85 kilobytes para más grande va directamente al montón de objetos grande. Cualquier objeto menor que este límite entra en el pequeño montón del proyecto. Tener estos dos montones separados resuelve el problema de objetos grandes y de larga vida. Ya no necesitan copiarse cuatro veces antes de que terminen en generación a, sino que van directamente al gran montón de objetos, que sólo se procesa en generación a y nunca se compacta. Y ahí lo tienes, el recolector de basura de redes para perros es una calidad de basura generacional o que utiliza un ciclo compacto de barrido marca . Cuenta con montones separados para objetos grandes y pequeños. Si lo piensas, el recolector de basura DOT Net hace algunas suposiciones muy específicas sobre objetos y vidas. En primer lugar, asume que los objetos serán de corta elevación o de larga duración. Todos los objetos de elevación corta deben asignarse, utilizarse y descartarse en un solo ciclo de recolección. Cualquier objeto que se deslice a través de las grietas, por así decirlo, queda atrapado en la generación uno en el siguiente ciclo. Por lo que cualquier objeto que sobreviva a los ciclos de recolección termina en generación a y debe ser un objeto vivo largo. Además, cualquier objeto de más de 85 kilobytes de tamaño siempre se considera un objeto vivo de larga duración. Al mirar la frecuencia de recolección fuera de las diversas generaciones, es evidente que el recolector de basura asume que la abrumadora mayoría fuera de los objetos será de corta duración. Por lo que puedo resumir mi consejo de optimización de memoria en una sola frase. No ir en contra de estos supuestos. Entonces, ¿qué hemos aprendido? El recolector de basura utiliza un barrido de marca y un ciclo compacto. El recolector de basura tiene dos montones separados para objetos grandes y pequeños. El gran montón de objetos en el pequeño objetivo. El objeto pequeño que utiliza tres generaciones, todos los objetos nuevos se asignan en generación cero y progresan hacia generación al objeto grande. Tiene una sola generación la cual se procesa junto con la generación a off del pequeño objetivo. Además, el montón de objetos grande no compacta el montón. Para optimizar la memoria libre, el recolector de basura hace dos supuestos importantes sobre los tamaños y las vidas de los objetos. Un 90% de descuento en todos los objetos menores de 85 kilobytes deben ser vidas cortas para todos los objetos mayores de 85 kilobytes deben ser de larga vida. 17. Consejos #7: optimiza para la recogida de basura: bienvenido a partes a off de la serie de conferencias sobre recolección rápida de basura. En esta conferencia, vamos a ver varias optimización del rendimiento es hacer que el recolector de basura funcione lo más rápido posible. Pero primero, recapitulemos lo que aprendimos sobre el recolector de basura. En la conferencia anterior, el recolector de basura de redes DOT utiliza un barrido de marca en ciclo compacto para limpiar D objetos referenciados del calor. Es uso es separar montones para objetos grandes y pequeños. El grande o embarcadero de este pequeño objeto, los objetos pequeños que utiliza. Tres generaciones, todos los objetos nuevos se asignan en generación cero ons. avance hacia generación a generación cero se recoge con mucha frecuencia. Generaciones una mano demasiado menos así. Generaciones ayudaron a limitar el número de objetos fuera en la generación cero. Cada ciclo de recolección borra completamente la generación cero de todos los objetos en el siguiente ciclo , el recolector de basura sólo tiene que inspeccionar los nuevos objetos que se crearon después del último ciclo. La primera optimización de rendimiento basada en memoria que vamos a ver es simplemente limitar el número de objetos que creamos en generación cero. Cuanto menos se creen objetos, menos trabajo tiene que hacer el recolector de basura para limpiar el montón. Existen dos estrategias que puedes seguir para limitar el número de objetos fuera de la cadera. El primero es asegurarte de que tus códigos no creen ningún objeto redundante en ningún lugar segundo para asignar, usar y descartar tus objetos lo más rápido posible para que ya sean los asignados por el recolector de basura en el siguiente ciclo. Si esperas demasiado tiempo entre asignar, usar en descartar tus objetos, corres el riesgo del final en generaciones una o dos. Por lo que para objetos de corta vida, quieres que tus abrigos estén lo más ajustados posible. Echemos un vistazo a algunos ejemplos. Aquí hay un fragmento de código que bucle 10,000 veces en construye una cadena, usando un constructor de cadenas con un método frío al apéndice. ¿ Se puede ver el problema con este abrigo? Te daré 10 segundos para que pienses. Aquí está la solución. El problema es con la concatenación de cadenas dentro del mensaje de anexaciones, recordarás que las cadenas son inmutables, y por lo tanto el mensaje de dos cadenas en la adición ambos crea objetos de cadena extra en el montón para cada bucle it oration el frío en la parte inferior evita este problema por montaje, llamando a upend dos veces. La diferencia es de 40,000 menos objetos de cadena en el montón. Esa es una gran mejora. Entonces aquí hay otro ejemplo. A ver si puedes avisar el problema. Te volveré a dar 10 segundos. Y aquí está la solución. Si almacena enteros en una lista de matrices, los enteros se empaquetan en la cadera. Las listas genéricas evitan este problema mediante el uso en matriz de enteros internos en lugar de una matriz de objetos, una simple modificación de los códigos que da como resultado 20 mil menos en caja en sus objetos en el bien. Un ejemplo más. un objeto pequeño inicializaun objeto pequeñoy estático, luego se ejecuta un montón de otro culto primero en. Por último, el objeto realmente se acostumbra. ¿ Qué tiene de malo esta imagen? Te daré 10 segundos, y aquí está la respuesta. El objeto es pequeño, pero la brecha entre la asignación y el uso es muy grande, por lo que hay una gran probabilidad de que los objetos terminen en la generación uno o dos antes de que se acostumbre. Los códigos en la parte inferior evitan este problema, mi primero, hacer que los objetos no estáticos allí, asignarlo es justo antes de su uso y finalmente establecer la referencia del objeto para saber justo después de su uso para señalizar a la basura coleccionista que se hicieron sobre que los objetos está listo para ser recolectados. Si no te gusta tener asignaciones en todos tus códigos, también puedes envolver los códigos inferiores en el mensaje, igual que el objeto de referencia salga del alcance al salir de los métodos. Esa es mi solución favorita. La siguiente optimización que puedes realizar es afinar la vida útil de tus objetos. El recolector de basura asume que eso es casi todo. Los objetos pequeños serán de corta duración, y todos los objetos grandes serán de elevación larga, por lo que debemos evitar lo contrario. Pequeño, elevación larga, todo tex o grandes en breve sujetos. Es instructivo ver estas combinaciones en una gráfica. Si trazar la vida útil del objeto horizontalmente en el tamaño del objeto verticalmente, obtengo los siguientes gráficos. Los cuadrantes inferior izquierdo y superior derecho están donde quieres estar. Estas combinaciones fuera de objetos, tamaños y tiempos de vida coinciden exactamente con los supuestos fuera del recolector de basura. El top izquierdo en la parte inferior derecha? Cuadrantes están en desacuerdo con las suposiciones fuera del recolector de basura. Si tu código contiene muchos objetos de estos cuadrantes, estás trabajando efectivamente contra las suposiciones fuera del recolector de basura en el rendimiento fuera de tu abrigo probablemente sufrirá como resultado de ello. Entonces, ¿qué podemos hacer para meternos en los cuadrantes correctos? Empecemos con los objetos. Vida útil para re factor grandes, objetos de elevación cortos que necesitamos para aumentar el objeto. por vida. Existe una estrategia muy simple para esto, que se llama pool de objetos. La idea es que en cambio muchas veces descartar y objetos y asignar y nuevos objetos. En su lugar, reutiliza los objetos existentes. Debido a que los mismos objetos se están utilizando una y otra vez, efectivamente se convierte en un objeto vivo largo. Esta es una estrategia muy popular para optimizar el montón de objetos grandes. Entonces, veamos un ejemplo. Aquí hay un fragmento de códigos que asigna una lista de matriz grande en, luego la usa dos veces, descartando en reasignar la lista entre usos. ¿ Cómo mejorarías este abrigo? Te daré 10 segundos para pensarlo, y aquí está la solución. En cambio, fuera descartar al reasignar la lista, en su lugar la limpias con una llamada al mensaje claro y luego reutilizas la lista para el segundo método, llama en el nuevo abrigo. El mismo array list objetos se utiliza una y otra vez, su vida útil se incrementa en los objetos efectivamente se convierte en una larga vida. Este cambio mejora el rendimiento y reduce la posibilidad de que el montón de objetos grande se fragmente. Ahora veamos el problema inverso. Tenemos un pequeño, largo levantar objetos que debemos refractar o en unos objetos de corta vida. ¿ Cómo funcionaría eso? Aquí tienes un ejemplo. Este abrigo llena una lista de array con 10,000 pares de objetos. Libras cada cabello contiene dos dentro del tuyo. Entonces, ¿qué tiene de malo este código? Te daré 10 segundos para pensarlo. El problema es que la lista de matriz es un objeto grande, por lo que pasa al objeto grande el que es asume que es de larga vida. Pero la lista está llena de pequeños objetos de par, 10.000 de ellos. Todos estos objetos van hasta que el objeto pequeño se acumula en Generación cero, porque la lista de matriz está manteniendo un dedo del pie de referencia cada Eisen. Todos estos padres nunca d referencia, y eventualmente todos se mudarán a una generación. A la solución es usar otra estrategia refectoria popular en su lugar, fuera tener una lista con dos enteros en cada elemento de lista. Rompimos la lista de partes en dos inter genera separados. Debido a que un entero es un tipo de valor, se almacenará con la matriz. Por lo que ahora sólo tenemos dos aumento más grande en el objeto grande él y absolutamente nada en la generación cero. Problema resuelto. El tercer optimización que puedes realizar es encontrar junio del tamaño de tus objetos. El recolector de basura asume que casi todos los objetos lentos serán de corta duración. Todos los objetos grandes serán de larga vida. Entonces si tenemos los opuestos en nuestros códigos, elevación larga escolar o objetos grandes cortos izquierdos, necesitamos refraccionar el tamaño de estos objetos para que vuelvan a entrar en la carga correcta. CONFORMIDAD. Empecemos con un gran objeto corto izquierdo para reducir el tamaño de este objeto. Hay dos estrategias. Uno dividió el objeto de partes en sub objetos, cada uno menor que 85 kilobytes o dos redujo la huella de memoria fuera del objeto. Aquí te dejamos un ejemplo fuera de la segunda estrategia. Un bucle llena el buffer con 32 miles, pero es ¿Puedes ver qué le pasa a sus códigos? Te daré 10 segundos, y aquí la respuesta. El bucle llena el buffer con picaduras, pero el buffer se define como una matriz fuera de enteros. El búfer contiene 32 mil elementos en Dado que un entero tiene cuatro bytes de tamaño, esto suma hasta 100 28 miles de picaduras. Esto está por encima del umbral de objeto grande, y por lo tanto este buffer va directamente hasta que el montón de objetos grandes en se recoge en generación a la solución es re factorizar el buffer ya que muerde buffer. Ahora las huellas de memoria fuera del búfer son exactamente 32,000 bytes, que es más pequeño que los umbrales de objetos grandes. Y así se pone tiendas en el pequeño objetivo en generación cero, igual que nosotros waas. Ahora, veamos el problema inverso. Tenemos un objeto pequeño, de larga vida que debemos re factorizar en un objeto de elevación grande, largo. ¿ Cómo funcionaría eso? La solución es, bien sea y grande la huella de memoria fuera del objeto o fusionar varios objetos juntos para crear unos objetos más grandes que puedan ir en el objetivo grande. Entonces aquí está el ejemplo final de esta conferencia, este abrigo declara una lista de matriz estática en. Entonces, en algún lugar a mitad de camino, el programa empieza a usarlo. ¿ Qué tiene de malo este código? Te daré 10 segundos. Aquí está la respuesta. Está claro que se pretende que el objeto sea un objeto vivo de larga duración porque se declara estático . Si también sabemos que la lista eventualmente contendrá al menos 85 kilobytes de días, ¿eh? Entonces es mejor inicializar la lista a este tamaño. Esto asegura que la lista vaya directamente al montón de proyectos grande porque si no inicializas la lista, obtiene la capacidad predeterminada, que de la parte superior de mi cabeza es de 16 kilobytes. Entonces la lista entra en el montón de objetos pequeños en generación cero y eventualmente pasa a generación a después potencialmente, habiendo sufrido cuatro operaciones de copia de memoria inicializando la lista al tamaño correcto de inmediato, se evita la migración de generación cero a generación a enteramente. Podría parecer extraño que puedas optimizar el abrigo haciendo más grandes objetos, pero eso es exactamente lo que estamos haciendo aquí. Y a veces realmente funciona. Entonces, ¿qué hemos aprendido para optimizar tu código de tal manera que el recolector de basura se ejecute rápido posible? Es necesario seguir estas estrategias primero, limitar el número de objetos fuera que crea segundos asigna uso en descartes objetos pequeños lo más rápido posible. Seward reutiliza todos los grandes proyectos. Se quiere trabajar con el recolector de basura y no en contra. Y así debes asegurarte de que todos los objetos de tus códigos sean pequeños y de corta duración. Cuatro vidas grandes y largas. Entonces si sucede que tienes objetos que son o bien de elevación exuberante y corto o pequeños en elevación larga , es posible que quieras volver a factorarlos para sujetos grandes en breve. Puedes o bien aumentar la guerra de por vida, disminuir el tamaño fuera del objeto y cuatro pequeños objetos de elevación largos. Puede disminuir el signo de vida o aumentar el tamaño. Todos estos cambios beneficiarán el rendimiento de tu abrigo. 18. Consejos #8: usa delegados rápidos: en esta conferencia, voy a echar un vistazo más de cerca. Eso son delegados. Ahora bien, si no estás familiarizado con el concepto fuera de delegados, los delegados no son más que un tipo que envuelve a un mítico. Entonces, donde un tipo regular define qué tipo de datos se debilita, almacena , digamos, transmite en tus fechas, etcétera, un delegado define a qué tipo de método podemos llamar. Entonces, ¿cuál es el método para los aficionados sobre cuál es el tipo de retorno a definir? Delegado en C. Sharp, utilicé las palabras clave delegadas como esta. Este ejemplo específico define una llamada al método que espera enterrar sus parámetros de entrada un parámetro de salidas inseguras en un tipo de retorno vacío. Ahora esta declaración no define ninguna variable todavía. Todo lo que tenemos en este punto es una nueva definición de tipo llamada agrega Delegado, que describe una firma de métodos particulares para crear un nuevo delegado. Variable. Necesito hacer esto. Esto configura una nueva variable de tipo off. Se trata de delegados. Entonces, ¿cómo firmo el valor a esta variable? Bueno, primero, necesitamos tener métodos en algún lugar de nuestro código que coincida exactamente con la firma que configuramos antes para insertar sus parámetros de entrada. Un entero sale parámetro y un tipo de retorno vacío algo como esto. Ahora que tengo la implementación de métodos, puedo asignar la variable delicates así y luego puedo llamarla así. Ahora Lo que has visto hasta ahora es llamar a un reparto único delegados. Este es un delegado que inicialicé con una implementación de un solo mensaje y cuando invoco a los delegados, llama a los métodos únicos. Pero los delegados son mucho más poderosos que esto. También puedo configurar un multielenco delegados. Se trata de un delegado que puede llamar a más de un método de una sola vez. Digamos que tengo que agregar métodos en mi abrigo llamado Es cálido y se suma a. Entonces puedo armar así a los delegados. Tenga en cuenta el uso off the plus es operador en la tercera línea. Esto, como un método extra a un delegados existentes en efectivamente configura un multielenco delegados. Cualquier delegado puede ser mullido simplemente agregándole más métodos. No hay límites a los métodos numéricos que puedes pedir, pero por favor no vuelvas a pasar por la borda. Invoco a los delicados así en esto llamarán a todos los métodos de adiciones en secuencia en el mismo orden que los agregué a los delicados. Entonces esto me hizo preguntarme. ¿ Existe una diferencia de rendimiento entre un reparto único y un delegado multielenco? ¿ En? ¿ Cuál sería la pena desactivada usando un delegado en lugar de una llamada a método regular? Vamos a averiguarlo. Para probar el desempeño de los delegados escribí el siguiente código. Empiezo aquí con los familiares delegados por sumar dos números juntos. Aquí abajo hay dos implementaciones fuera de las manos métodos que son completamente idénticos y coinciden con la firma de los delegados. Después ejecuto tres pruebas en mi primera prueba. Llamo directamente a los métodos de anuncios. Esto proporcionará una línea de base. En la segunda prueba se configura un reparto único. Delegados en lo llama dos veces. Esto nos dirá cuál es la sobrecarga desactivada, utilizando a los delegados en lugar de llamar directamente a los métodos de anuncios. Y por último, envié un delegados multi personalizados, lo asigno con dos métodos y luego invoco eso delicado. Una vez. Esto comparará el rendimiento fuera de multi cast en delegados de reparto únicos, y aquí están los resultados. Hay una sobrecarga de rendimiento del 9% cuando se usa un delegado en lugar de llamar directamente al manejador , eso no es tan malo, pero usar un multi cast delegados es más del doble de lentitud que llamar a un único como delegados dos veces. Whoa. El motivo de esto es por cómo se implementan los delicados. Detrás de bambalinas, un delegado es implementado por la clase de delegados multielenco. Esta clase está optimizada para delegados uni elenco. Utiliza un mensaje en una propiedad objetivo para llamar directamente a un solo método. Pero para los delegados multi cast, la clase utiliza una lista de invocación en listas genéricas internas, contiene referencias a cada método, y se les llama en secuencia los gastos generales fuera. Pasar por la lista de invocaciones es lo que provoca esta gran diferencia en el rendimiento. Por último, permítanme ofrecer una palabra de consejo. A veces veo frío así. Esta línea de código define una variable demagogue on, luego inicializar. ¿ Es con un métodos DouBie que no hace nada. El beneficio de preinicializar la delicada variable es que ya no tenemos que comprobar si la variable es no. Entonces, en lugar de hacer esto, ahora podemos simplemente hacer esto independientemente. Si se ha inicializado a los delegados, el código siempre funcionará. Los delegados o llamarán al mensaje ficticio en no hacer nada o llamarán a los líos ficticios en Duthie firma mensaje en secuencia. Pero por favor no hagas esto. Ya has visto en las mediciones de rendimiento que los delegados de multitransmisión son mucho más lentos que únicos como delegados. Por lo que el simple acto off pre inicializar la variable delegada en realidad hace que el pelaje sea más del doble de lento. Entonces aquí está mi consejo sobre delegados rápidos en C agudo. El overhead off de desempeño usando los delegados es de sólo 9%. En la mayoría de los casos, esto es perfectamente aceptable, y así que siéntete libre de usar delegados en cualquier lugar de tu abrigo donde sea conveniente Aliento si debes tener el mayor rendimiento posible, luego retira a todos los delegados de misiones críticas secciones frías y agarrar ese extra 9% de descuento rendimiento. Y siempre debes evitar usar delegados multi elenco en Mission Critical Coast porque son más del doble de lentas, tan únicas como los delegados. 19. Consejos #9: construye una fábrica de clases rápidas: en esta conferencia, voy a ver si puedo acelerar un abrigo muy común. Construir la fábrica de clase. Ahora tal vez te estés preguntando, ¿qué es exactamente una fábrica de clase? En pocas palabras, una fábrica de clases es una clase especial que construye otras clases bajo demanda en base a algún tipo de datos de configuración externa. A menudo se ven fábricas de clases que se utilizan al acceder a bases de datos en códigos. Simplemente queremos acceder a la base de datos en. Realmente no nos importa si los datos se almacenan en una base de datos de servidor de secuela de Microsoft en base Oracle o en la base de datos Maya Scwill. Ese es un detalle de implementación del que los códigos no deben tener que preocuparse. Entonces en los códigos, podrías ver algo como esto. En este ejemplo, The Class Connection factory es una fábrica de clases que sabe crear un objeto de conexión de base de datos para los datos de ventas. Entonces, si hay una configuración en algún lugar de un archivo de configuración que especifica que todos los datos de ventas se almacenan en una base de datos de artículos, entonces la fábrica de clases puede usar esta información para devolver un punto de datos del sistema. Los clientes de Oracle punto clase Oracle Connection. Se puede ver cómo las fábricas de clase, simplemente cinco el frío enormemente. Ya no tienes que preocuparte por dónde se almacenan ciertos datos. El clase-factory sabe on devolverá automáticamente los objetos de conexión correctos para cada solicitud. Las fábricas de clase tienen otra gran ventaja. Nos permiten mover datos en tiempo de ejecución si, encierto momento, cierto momento, los datos de ventas migran a una base de datos mi SQL, todo lo que necesitamos hacer es actualizar el archivo de configuración en la siguiente solicitud de datos, la fábrica de clases lo hará leer los datos de configuración modificados y devolver A Mis objetos de conexión SQL en su lugar Para construir una fábrica de clases, es necesario completar este fragmento off coat. Por lo que las entradas es una cadena que contiene el tipo fuera de la clase a ser. En cambio, ella ated en la salida deseada es una instancia real fuera de esa clase ahora, este es en realidad un problema difícil de resolver. Lo más cercano que puedas conseguir eso es compilar. El tiempo es algo así. Esto funciona muy bien porque el compilador puede compilar cualquier escenario posible que podamos encontrar en tiempo de ejecución. Pero la desventaja es que tenemos que anticipar de antemano cada uso posible fuera de la fábrica de clases . Por ejemplo, es completamente imposible mover los datos de ventas a una base de datos mi SQL si no hemos anticipado el uso fuera de mi SQL con unas declaraciones de caso correspondientes delante de las manos. Por lo que este abrigo no es muy útil en una fábrica de clase, pero sí tiene el mejor rendimiento posible. Añadiré el código a mi medición más adelante para servir como valor de referencia. De acuerdo, así que un interruptor de declaraciones no será suficiente. ¿ Qué más tenemos? Otra posibilidad es usar la reflexión. El sistema de reflexión adulta, espacio de nombres tiene una clase muy bonita llamada activador que podemos usar para construir cualquier tipo de objetos fuera que nos gusten en base al nombre de su tipo. Por lo que puedo reescribir el método anterior para usar la reflexión en su lugar, y se verá algo como esto. Ahora esto va a funcionar perfectamente en todos los escenarios, puedo lanzar cualquier tipo de nombre que me guste. Si el tipo existe, la clase activador construirá la instancia de clase correspondiente. Es perfecto. No obstante, este resfriado tiene un gran problema. La reflexión es realmente lenta. Ya veremos qué lento en un momento en que corra el benchmark, pero confía en mí cuando digo esto va a introducir un enorme revés de rendimiento en tu abrigo esto no siempre tiene que ser un problema. Podemos suponer que las conexiones de bases de datos no se van a crear con tanta frecuencia. Además, abrir una conexión a base de datos es una operación mucho más lenta que construir la clase, por lo que una ralentización en la construcción apenas va a ser perceptible para el usuario final. Debido a esto, a menudo se ve la clase de activador apareciendo en C abrigo afilado en capa no crítica. No hay absolutamente nada malo en usar un poco de reflexión para adaptarse a tus necesidades. Pero, ¿qué hay de frío crítico, donde el máximo rendimiento es crucial? Afortunadamente, hay otra solución. Te voy a mostrar un truco de soplo mental para construir cualquier tipo de clase en tiempo de ejecución con un rendimiento que es comparable a las declaraciones de cambio. Echa un vistazo a esto. En primer lugar, volvamos al problema básico que necesitamos para completar este método. Se trata de un método genético que puede construir en cualquier momento, pero una colección de métodos específicos también estaría bien. Entonces digamos que necesito construir una clase llamada mi clase tipo uno. Entonces necesitaría el siguiente mensaje en lenguaje intermedio común. El cuerpo fuera del mensaje va a verse así. Entonces aquí está el invitado fuera del truco. Voy a crear dinámicamente un delegado en tiempo de ejecución a la derecha estas dos instrucciones de lenguaje intermedio en él y luego llamar a los delegados para crear la instancia de clase. Así es como funciona. Mi fábrica de clases recibe primero la cadena de nombre de tipo, que describe el tipo fuera de la instancia de clase a crear. Lo primero que hace la fábrica de clases es comprobar en un diccionario si ya se han creado los delegados correspondientes . Si es así, la fábrica recupera a los delegados del diccionario y lo llama directamente para crear la clase. Instancia incluso sabe que la fábrica crea un método dinámico en escribe los nuevos objetos y devuelven instrucciones en ella. Después envuelve los métodos en un delegado y almacena a los delegados en el diccionario en caso lo necesitemos de nuevo más adelante. Por último, llama a los delegados a crear la instancia de clase. Crear un método dinámico delicates es realmente lento, incluso más lento que llamar a la clase activador. Pero aquí está la cosa. Una vez que tengamos un delegado, podemos seguir llamándolo una y otra vez para crear más instancias de clase. Andi llamar a un delegado es mucho, mucho más rápido que usar la clase activador, por lo que solo habrá un retraso al crear los primeros objetos de un tipo determinado. Cualquier carbón posterior va a ser súper rápido. De acuerdo, entonces echemos un vistazo a mi programa de benchmarking para esta conferencia. Se puede ver en esta constante declaración aquí arriba que me voy a interesar, y ella se comió un millón de objetos. El primer mensaje mide un bucles un millón de veces en utiliza las sentencias de switch estático para construir los objetos que se pueden ver aquí. Que mi programa crea instancias de constructor de cadenas en realmente no soporta nada más. Por lo que las declaraciones de switch son un poco tontas porque solo admite una sola clase on no se puede modificar de ninguna manera después de la cópulación. Pero vamos a mantener su aquí para que sirva como valor de referencia. Es significa el mejor rendimiento posible a la hora de crear un millón de objetos. siguiente es Measure B, que utiliza la clase activador para crear los constructores de cadenas realmente simples y limpios códigos en. Es realmente una lástima que no vaya a funcionar muy bien. Vas a ver en un momento lo lenta que es realmente la reflexión. Por último, aquí está Measure C. Utiliza una fábrica de clases llamada gets class creator. Esta fábrica de clases devuelve un creador de clases, que se puede ver en realidad es un delegado para un método sin ningún parámetro no devuelve un objeto. La siguiente línea crea estos constructor de cadenas simplemente llamando a los delegados. Entonces toda la magia sucede en la fábrica de clases. Lo primero que hace la fábrica de clases es intentar recuperar a los delegados del diccionario . Si el diccionario aún no contiene los delegados en los que se mueve la fábrica obtiene la información del constructor para el constructor de cadenas. A continuación, crea un método dinámico. Dan's escribe el nuevo objeto en las instrucciones de devolución en el método. Por último , envuelve los métodos en un delegados en tiendas. Los delegados en el diccionario finalmente aquí abajo los principales métodos del programa llama a los tres métodos de medida y muestra el tiempo propio en milisegundos. De acuerdo, veamos cómo se miden estas tres técnicas. Yo voy a ejecutar el programa. Echa un vistazo a esto y aquí los resultados. El comunicado switch se ejecutó en 131 milisegundos. El activador clase talk 11,377 milisegundos en el método dinámico talk 673 milisegundos. Estos son los resultados en una gráfica. Por lo que la declaración de switch construyó un millón de constructores de cuerdas en 131 milisegundos. Esto corresponde a 7633 objetos creados par milisegundos, pero la clase activador hizo mucho, mucho peor. Tomó una friolera 11,377 milisegundos para construir los constructores de cuerdas, lo que corresponde solo a 87 objetos durante milisegundos. La clase de activador es 86 veces más lenta que las sentencias switch. Y por último, aquí están los resultados del mensaje dinámico. El delegado de métodos dinámicos construyó un millón de constructores de cadenas en 673 milisegundos. Esto corresponde a 1485 objetos durante milisegundos, por lo que los métodos dinámicos son cinco veces más lentos que las sentencias switch. Pero es casi 17 veces más rápido que la clase de activador. De acuerdo, entonces, ¿qué hemos aprendido? Una fábrica de Clase es una clase que construye otras instancias de clase bajo demanda utilizando información de configuración externa . Una forma común de implementar fábricas de clases es con la clase activador, pero la clase de activador es 86 veces más lenta que la compilada estática. Abrigo. Una solución mucho mejor es usar métodos dinámicos, delega los métodos dinámicos. Los delegados son sólo cinco veces más lentos que los códigos compilados estáticos en 17 veces más rápidos que la clase de activador. En otras palabras, si reemplazas la clase de activador por delegados de mensajes dinámicos en tu fábrica de clases, no vas a acelerar tu abrigo por un factor apagado 17. 20. ¿Están los arrancados en la stack valen los problemas?: En el último par de conferencias, hemos analizado las clases de colección, incluyendo la lista de array, la lista genérica sobre varios tipos de criar nativo, unidimensionales, matrices unidimensionales, bidimensionales y dentadas. Hay una cosa que todas estas clases de cobranza tienen en común. Todos viven en el montón. Pero ¿y si te dijera que en realidad es posible en C? Puntera afilada asigna en array de entrevistadores directamente en la pila, teniendo una colección fuera de enteros fuera de la pila como sus ventajas. Si miras el montón, el montón es memoria administrada. Esto significa que el recolector de basura de vez en cuando mueve bloques de memoria alrededor del dedo del pie optimizar el espacio libre. Pero esta pila funciona de manera diferente. memoria en la pila se asigna cuando su código ingresa a un método como D asignado. Cuando regresas de un método asignado, la memoria nunca se mueve. Esto tiene una ventaja importante. Si de alguna manera pudiéramos obtener un puntero para apilar la memoria, permanecerá vigente hasta que la memoria de pila sea la asignada. Pero conseguir un puntero para calentar la memoria es mucho más difícil porque el recolector de basura puede mover la memoria a otro lugar en cualquier momento dado para obtener un punto o dos memoria de calor con tienen que arreglar un bloque de memoria en su lugar en el montón, diciéndole al recolector de basura No se puede tocar este bloque fuera de la memoria hasta que termine con ello todo esta administración de memoria crea una sobrecarga de rendimiento, por lo que no es irrazonable esperar que ese punto de operaciones en la pila sea más rápido que el mismo punto de operaciones en el Así que veamos algún código de nuevo. Aquí está el programa que utilicé anteriormente para comparar el rendimiento fuera de la lista de matriz la lista genérica en la matriz nativa. Pero he añadido otro método de prueba en la medida inferior D. Echemos un vistazo. Empiezo declarando sobre códigos inseguros. Bloque en C. Punto afilado de operaciones solo son posibles dentro de bloques inseguros, por lo que este es un paso obligatorio. Entonces uso estas palabras clave pegadas Una mirada para asignar en array off 10,000 enteros directamente en la pila. El valor de retorno de esta operación está en Interviewer Pointer que apunta y directamente a la memoria de pila. Entonces dos anidados sueltos. El bucle externo repite los experimentos 10,000 veces. El look interno recorre todos los 10,000 elementos fuera de la matriz y la ciencia de todos y cada uno de ellos. Se ve que a pesar de que la lista es un entrevistador, Pointer, la sintaxis, acceso del dedo del pie y los elementos es exactamente la misma que si la lista sería una matriz entera. Se borran los corchetes. Intacs todavía funciona aquí. Aquí arriba está los Métodos de Medida C. Este método hace exactamente la misma operación, pero utiliza un array entero regular en el montón para que podamos comparar el rendimiento. Vamos a ejecutar los códigos. Aquí vamos. El entero regular de Ray tarda 808 milisegundos en ejecutarse, pero la matriz Stack solo necesita 764 milisegundos. El array Stack es de cinco puntos, 4% más rápido. Echemos un vistazo detrás de bambalinas y veamos qué está pasando en lenguaje intermedio. Cambiaré los entornos a puntos de ruptura del centro del modo de depuración, volveré a ejecutar el código y luego cambiaré a vista de desmontaje para que podamos ver las instrucciones del idioma intermedio encendido. Y aquí están los códigos para las asignaciones de matriz de enteros regulares. Se ve que este es el arreglo familiar fuera de tres instrucciones de ubicación de carga y un elemento conjunto. La variable de lista se almacena en la ubicación de la variable. Uno en se carga en la pila de evaluación, luego el I Valioso se carga dos veces, una como el índice de matriz y otra como el valor a establecer. Por último, la construcción de elementos conjuntos almacena el valor en el Índice dado. Ahora veamos la operación Poynter en códigos gestionados. Si me desplaza, aquí están las mismas asignaciones en los métodos de medida D. En primer lugar, el puntero de lista se carga en la pila de evaluación, Luego se cargan la variable I en el número cuatro, multiplican juntos y se suman al valor de lista. Esto tiene sentido si se considera que un entero es cuatro picaduras en la memoria, el pelaje simplemente está calculando la dirección de memoria a la que escribir, que es lista más por qué multiplicado por cuatro. El siguiente instrucción es load Location tres, que carga el valor a escribir en la pila de evaluación. Y por último, tenemos una tienda en instrucción directa al respecto. La instrucción escribe el valor directamente en la memoria. Entonces, tal vez sorprendentemente, estos siete puntos o instrucciones en realidad corren más rápido que las cuatro instrucciones de matriz en la medida. C Método en la diferencia se debe a que los derechos de instrucción de los elementos establecidos en la memoria administrada , mientras que la tienda en instrucción directa escribe directamente en una dirección de memoria fija sin tener que quitar ningún tipo de administración de memoria en cuenta. Por último, permítanme volver a ejecutar el programa. Pero esta vez, con todas las mediciones, métodos habilitan, voy a comparar la lista de array, la lista genética, la matriz de entrevistas nativas, las manos del entero o un asignadores en la pila. Aquí vamos. Y aquí están los resultados. Déjame mostrarte un gráfico fuera de los resultados, para que sean fáciles de comparar. La lista de arreglos tarda 9314 milisegundos en ejecutarse. El listado genérico es mucho más rápido. Eso son 2704 milisegundos. El array entero es aún más rápido. Eso son sólo 745 milisegundos. Pero esta matriz de pila vence a todos con un tiempo de ejecución apagado solo siete cientos y 13 milisegundos. La diferencia en el rendimiento entre la matriz entera nativa en la matriz en la pila es 4%. Esto es mucho menor que la diferencia entre, digamos, la matriz entera en la lista genérica, que es del 72%. Entonces, si optimizas algunos códigos reemplazando una lista genética por la matriz nativa, que ya te da un aumento del rendimiento del 70%, vale la pena ir aún más lejos y convertir la matriz en una matriz basada en pila solo por la ganancia extra de sólo 4 a 5%. Probablemente la respuesta no lo sea. La diferencia en el rendimiento es tan pequeña que podría ser fácilmente una fluke de medición. Para probar ese escenario, hice un par de modificaciones a mi programa. Echemos un vistazo. Simplemente encuentro que el programa solo mire la matriz regular en la pila neccesary cada método de mediciones espera parámetro que indica el número fuera de elementos de matriz a asignar. La medición se repite 5000 veces en cada método regresa al tiempo total de ejecución en milisegundos. Aquí abajo están los principales métodos del programa, con el bucle que repite el experimento para los números de diferencia fuera de los elementos de matriz. Entonces, ¿cuáles son los resultados? En realidad preparé los resultados de antemano en ponerlos en una gráfica. Entonces aquí está el rendimiento para ambos tipos de la carrera para los tamaños de carrera de 0 a 49 elementos. Bastante inconcluyente, ¿ dirías? Pero tal vez la diferencia de rendimiento se convierta en significación como tamaños de matriz más grandes. Entonces vamos a probar eso también. Aquí están los resultados de nuevo, pero ahora, para una carrera tamaños entre un miles en 100,000 elementos otra vez inconcluyentes. No hay ganador claro. Entonces, ¿cuándo debes usar Stack basado en la raza? La respuesta es casi nunca, ciertamente no para la optimización del rendimiento, las mejoras son diminutas, solo un poco por ciento, y bastante a menudo la matriz basada en pila es en realidad más lenta que una matriz basada en calor regular. Eso. El stack es de tamaño limitado. Las matrices basadas en pila solo pueden almacenar hasta 10 megabytes de datos antes de que se quede sin Stacks Base. Para darte una idea, intenté asignar 10 millones de enteros en la pila. Tengo una excepción de desbordamiento de pila. Entonces tal vez te estés preguntando, ¿Por qué esta pila es un candado? Palabras clave incluso disponibles? Bueno, la respuesta es proporcionar en interfaz a otros lenguajes de programación que asignan matrices en la pila. Cuando estás escribiendo abrigo de costa que tiene que interactuar con el Windows un P I o bibliotecas escritas en C o C plus, más el stock. Alex Palabras clave te permite configurar un búfer de memoria como una especie de puerta de enlace entre esos idiomas en el punto net administrar la memoria. Por lo que en conclusión, solo use apilar un candado para la interconexión con abrigo no administrado encendido. No lo utilices para optimizar una matriz porque probablemente no vale la pena. 21. ¿Cómo puedo usar puntos en C#?: en esta conferencia, ahora voy a hablar de punteros. Los punteros no se usan mucho, pero el lenguaje C Sharp a sí los soporta, pero por defecto el punto de operaciones están prohibidos porque son potencialmente peligrosos. Si usas punteros en tu abrigo, el tiempo de ejecución de la rosquilla ya no puede garantizar que la memoria nunca se corrompa. Y es por ello que están prohibidos por defecto. No obstante, las operaciones puntuales están disponibles. Si entra en las propiedades de su proyecto en habilitar abrigo inseguro, entonces puede utilizar las siguientes declaraciones y declaraciones en su abrigo. Las palabras clave inseguras habilita el punto de operaciones. Se pueden utilizar las palabras clave para marcar bloques fuera de la capa que usan punteros. O puedes poner las palabras clave en una declaración de método para hacer que esos métodos enteros sean inseguros. Cuando declaras una variable agregando y Asterix al final del tipo, declaras un puntero fuera de ese tiempo dado. Entonces en este caso estoy declarando un punto de mordida o las palabras clave fijas, alfileres y objetos en la memoria, diciéndole al recolector de basura que no mueva los objetos en el calor hasta que terminemos con él. Debido a que el objeto es fijo, puede utilizar el operador de uno por ciento para obtener la dirección de memoria de los objetos. Entonces en este ejemplo, estoy fijando en objetos en la variable Q en el calor, y luego obtengo su dirección con el operador y por ciento, y luego un signo de esa dirección de memoria al puntero bys en la variable p, el Asterix operador de referencias appointer, lo que significa acceder a los datos como esa ubicación de memoria. Entonces en este ejemplo, estoy leyendo una sola mordida en la dirección actual fuera del puntero. También puedes tratar punteros como una carrera. El funcionamiento del indexador lee los datos en los descensos a la dirección del puntero de corrientes. En este ejemplo, estoy leyendo una sola mordida en la ubicación de la memoria. Dos picaduras por delante de la dirección actual fuera del punto. O puede cambiar la dirección de memoria fuera del puntero simplemente agregando o restando de su. En este ejemplo, estoy actualizando la dirección de memoria actual fuera del puntero adelantándolo tres picaduras por delante. Punto de apoyo se sumó al tiempo de ejecución dominante por tres razones. Uno dos ayudantes de interoperabilidad con código no administrado para soportar un C o C plus. Además no eso es compilarlo y tres para que sea fácil punto de puerto de los algoritmos basados para ver nítido. Por lo que se deben usar punteros en C Sharp o simplemente apegarse a una carrera. Bueno, vamos a averiguarlo. El ejemplo clásico para usar punteros en C. Sharp es cuando se hace manipulación de imágenes. Las imágenes, que se cargan en la memoria son administradas por el sistema operativo. El dato de la imagen está en memoria no administrada como no en el montón de oscuridad. Por lo que sólo hay dos formas de interactuar con los datos de la imagen. Están usando pueblos de alto nivel get pixel, establecer métodos de pixel o mediante la obtención de un puntero a la memoria no administrada. He escrito un pequeño programa que carga imagen en la memoria y luego realiza una transformación en escala de grises en su Vamos a echar un vistazo. Empezaré con los principales métodos aquí abajo. Mi imagen de prueba es una imagen de 19 70 modelo lane, aseverar Burke. Esta imagen es el estándar de oro para probar algoritmos de procesamiento de imágenes. Me encanta la imagen en la memoria, luego llamó a la medida un lío es realizar la conversión en escala de grises usando el get pixel y establecer métodos excell y luego escribir los resultados de vuelta al disco. Entonces vuelvo a hacer todo el asunto, pero esta vez llamo a la medida Be Message, que utiliza punto de operaciones para realizar la conversión en escala de grises hacia arriba. Estos son los métodos de conversión. Medida A recibe la imagen como un parámetro bucles a través de cada píxel. Usando estos 24 pierden contra el color fuera del píxel con un get pixel médico. Aquí la fórmula realiza la conversión a escala de grises. Entonces creo un nuevo valor de color, usando la variable escala de grises y modificando el píxel de corrientes llamando a Seth Pixel. Ahora compare eso con la Medida B. Empiezo con una llamada para bloquear bits la cual solicita acceso al dater de imagen sin procesar. Entonces, en este bloque de autos inseguros, obtuve un puntero de mordida a los datos de la imagen llamando a los dos métodos de puntero. La fórmula de conversión en escala de grises está aquí señala que utilizo una sala de índice para obtener los valores rojos verde y azul en la dirección de punto actual en y una y dos picaduras por delante de la dirección actual. Este siguiente bloqueo de códigos escribe el valor de escala de grises en los valores de píxel rojo, verde y azul escribiendo repetidamente en el punto de dirección actual al avanzar el puntero por una mordida. Y finalmente desbloqueo los datos de la imagen al devolver el tiempo de ejecución en milisegundos. Entonces, ¿qué método va a ser más rápido, Tú crees? Déjame ejecutar el programa y averiguarlo ahora. Como dije antes, estoy ejecutando un programa de captura de pantalla en este momento en este programa pone presión sobre la CPU en memoria libre disponible. En esto distorsiona los valores de rendimiento. Por lo que en realidad preparé esta sección con anticipación y puse los valores de rendimiento no distorsionados en una gráfica. Aquí están los resultados ejecuto la prueba tres veces como el tiempo de ejecución para realizar una conversión en escala de grises . uso de get pixel y set pixel está entre 146 ons 184 milisegundos. Pero al usar punteros, el tiempo de ejecución cae a solo siete milisegundos. Para las tres pruebas, el punto de operación es un abrumador 96% más rápido que el método de pixel del auricular get pixel . Por último, permítanme mostrarles los resultados. Abriré un navegador de archivos en seleccionar la imagen original y la imagen se ve así. está aquí las salidas fuera de la medida A y la Medida B. Como puedes ver, es una conversión perfecta en escala de grises en las salidas apagadas. Ambos métodos son idénticos. Por lo que hemos mirado la manipulación de imágenes en C nítida y hemos descubierto que los punteros dan un enorme impulso al rendimiento del 96%. ¿ Es aceptable usar punteros? En este escenario, la respuesta es sí. Siempre se utiliza el principal quitador para esta conferencia. Punteros para la manipulación de imágenes en red de puntos. Realmente no tienes opción porque los datos de la imagen están en memoria no administrada. En la alternativa get pixel y set pixel los métodos son demasiado lentos. Puedo generalizar este consejo en los siguientes lineamientos. En primer lugar, si estás interactuando con datos en memoria no administrada, intenta usar métodos get and set de alto nivel para leer y escribir los datos. Esto evitará tener que usar las palabras clave inseguras en Marque su Asamblea como inseguro. Si esta ruta no es aceptable porque necesitas leer y escribir una gran cantidad de datos sobre los overheads off los métodos individuales get y set se vuelven demasiado grandes. Zen. Obtener un puntero a los datos en lugar de modificar la memoria directamente para la manipulación de imágenes . Utilice siempre punteros porque los días de imagen siempre seré demasiado grande para cualquier otro tipo de acceso de alto nivel. Es perfecta práctica aceptada para la manipulación de imágenes. Bibliotecas escritas en C afiladas para ser inseguras 22. Consejos #10: aceleren el código con punteros: En la conferencia anterior, miramos el uso de punteros para la manipulación de imágenes en C sharp. Ahora, la manipulación de imágenes califica como un escenario en el que estamos interactuando con código no administrado. El sistema operativo carga la memoria de imágenes en su propio montón no administrado. Y la única forma en que podemos llegar a los datos de la imagen es solicitando un punto de mordida o así para este escenario específico. Claro, podemos usar punteros. Pero, ¿qué pasa con otros casos de uso? ¿ Es útil implementar estructuras de datos como listas vinculadas y árboles binarios con punteros ? O debería ser simplemente apegarse a una carrera Para averiguarlo? Necesitamos comparar con precisión el rendimiento fuera de las operaciones de matriz y las operaciones de puntero para ver si hay alguna diferencia entre ellas. Entonces imaginemos por un momento que todo mi sistema operativo está escrito en C sharp. Estoy ejecutando estos códigos en una macro pro, y voy a fingir que el sistema operativo siempre X está escrito en C sharp . Entonces cuando carga sobre la imagen en la memoria, los datos de la imagen están inmediatamente disponibles como un gestionado por tary. Por lo que he escrito un segundo programa basado en este supuesto. Echa un vistazo a esto Empecemos con los primeros métodos de medición A. Mi matriz de mordeduras está aquí arriba, Andi. Voy a fingir que se trata de los datos de imagen cargados en esta matriz. Yo recorro la matriz en convertidos, cada píxel en escala de grises, usando la fórmula familiar. Por lo que este culto utiliza la indexación tradicional de matriz para convertir cada píxel. Ahora aquí está el mismo código reescrito usando punteros. Usé las palabras clave fijas para pellizcar la matriz en la memoria, y luego obtengo la dirección de los primeros elementos en la matriz con el operador 1%, y asigné esa dirección al puntero de mordida en la variable P. El resto de la capa es exactamente lo mismo que los códigos basados en matriz. Miro por encima de cada píxel con esta variable por y usé indexación de puntero para leer y escribir cada uno por su valor. Y por último, aquí hay otro punto de los métodos basados. Pero en lugar de apagado, usando el acceso del dedo del pie de indexación, cada píxel simplemente avance el puntero en sí. Cada bucle interacional solo lee los rojos verdes sobre valores azules en, los modifica en escala de grises. Entonces avanzé el puntero por tres picaduras para acceder al siguiente píxel, lo que todavía hay alguna indexación en este método, pero solo por 01 o dos picaduras. El método principal aquí abajo llama a los tres métodos para variar tamaños de imagen. Medido el rendimiento para imágenes simuladas fuera de 512 hasta 4096 píxeles de ancho en pasos fuera 100 28. Ahora, como mencioné antes, el software de captura de pantalla distorsiona mis mediciones, por lo que preparé los resultados con anticipación. Estos son los resultados. Como puede ver, no hay mucha diferencia entre Measure A Measure B, usando una matriz de plaga o un puntero de compra con indexación, tiene un rendimiento más o menos igual. Pero mira la Medida C consistentemente más rápida que las otras dos en un 25% en promedio. Entonces, ¿por qué la medida es ver tanto más rápido? Para averiguarlo, necesitamos echar un vistazo al código intermedio. Entonces permítanme asegurarme de que el proyecto esté en modos de depuración. Voy a poner un punto de ruptura en los métodos principales, luego alrededor del programa en interruptor a vista de desmontaje. Entonces aquí vamos. Empezaré con la medida, un mensaje que utiliza un regular gestionado por tary. El código que modifica los píxeles está aquí. Se puede ver. Son las conocidas tres instrucciones de carga y luego una instrucción de elemento de la tienda para modificar los elementos de la matriz. El array de imágenes está en la ubicación cero. El índice variable I está en ubicación a, ya que el valor a escribir está en la ubicación tres. Esto lo hemos visto muchas veces. A estas alturas. Esta línea de códigos modifica el siguiente valor de color. Es casi el mismo abrigo, pero la variable I es incriminada por uno con estos tres ubicación de carga para cargar Constante uno en instrucciones de adición en la tercera línea de códigos es casi la misma. Pero ahora la variable I la incrementa en dos. Y finalmente, por aquí, la variable I es incriminada por tres con estos cuatro ubicación de carga baja ums constantes en instrucciones de ubicación de la tienda. Entonces, ¿cómo se ve este abrigo cuando estamos usando punteros? Déjame desplazarme hacia abajo hasta la medida, ser métodos y encontrar las líneas correspondientes de capa Primero, el puntero P se carga con esta ubicación de carga a la instrucción, luego la variable. Se carga con esta nota Ubicación se suman tres instrucciones sobre facturas para obtener la dirección de memoria a la que escribir. Por último, el valor a escribir se carga con esta ubicación de carga para la instrucción. El almacén final en instrucción directa realiza el derecho real a la memoria. Ves que esta secuencia de instrucciones fuera es casi la misma, excepto que hay un extra en la instrucción para calcular la dirección de memoria y que el valor se escribe con una tienda en instrucción directa en lugar de una instrucción de elemento de tienda . Las siguientes dos líneas son casi las mismas, excepción de la instrucción de adiciones extra para ya sea agrega una o dos a la variable I. Ahora echemos un vistazo. Eso es una Medida C. Escribir a la memoria es sólo estas tres instrucciones ama la ubicación. Tres. Para cargar los puntos son ubicación de carga cinco a mínimos. El valor de escala de grises Andi almacena indirecto para escribir en la memoria. En las siguientes líneas se tienen estas dos instrucciones extra de carga constante uno y se suma al incremento designado por uno y la constancia de carga también, y se suman para incrementar el puntero en dos. Por último, el puntero es incrementarlo por tres con este frío aquí, que es idéntico al culto por incriminar la variable I en los otros dos métodos. Entonces, ¿por qué esto se resfríos más rápido? Bueno, para empezar, este pelaje tiene menos instrucciones de lenguaje intermedio que los otros dos métodos medir ser necesarios. Cinco en operaciones para modificar la matriz donde este culto sólo necesita agregar operaciones y medir unas necesidades. Tres instrucciones de ubicación de carga antes de llamar a los elementos de la tienda donde este abrigo solo necesita instrucciones. También, el punto Tradiciones son mucho más pequeñas en medida ser. Tenemos que sumar la variable I al punto de P para obtener la dirección de memoria como el final fuera del bucle, yo crece a valores muy grandes hasta 48 millones. Compara esto con los métodos. Vea dónde está el puntero. único incremento es por 12 o tres picaduras. adiciones de números pequeños son mucho más eficientes que las adiciones de gran número porque el uso de CP tiene instrucciones especializadas para el incremento del valor por uno. Entonces, ¿qué podemos decir? En conclusión? Cuando está usando punteros valets Choice. Hemos visto que logró un aumento en el punto índice de operaciones realizadas por igual en punto neto. Pero para valores de índice muy grandes, es mucho más eficiente incrementos appointer y leer y escribir datos cerca de la dirección de memoria de puntos o corrientes. Y así este es el principal quitador para esta conferencia. Normalmente, no debes usar punteros en C agudo porque las ganancias de rendimiento simplemente no valen la pena . Los punteros de índice y las matrices unidimensionales regulares tienen igual rendimiento. Para evitar tener que usar abrigo inseguro, debes escoger una carrera. ¿ Cómo es en escenarios específicos? El punto de algoritmo basado puede superar a un algoritmo basado en matriz hasta en un 25% para estos escenarios, Las siguientes condiciones deben cumplirse. En primer lugar, estás leyendo y escribiendo un gran bloque de datos fuera de al menos 10 megabytes de tamaño o más grande. En segundo lugar, el algoritmo escanea cada mordida secuencialmente o salta a través de los datos de una manera predecible y espadas durante cada bucle. Iteración sólo los datos muy cercanos a la dirección del puntero actual necesitan ser leídos o escritos. Si tu algoritmo se adhiere a estas condiciones y no te importa producir un ensamblado inseguro , siéntete libre de usar punteros y recoger el 25% extra en rendimiento 23. Recap del curso: felicitaciones. Has completado todo el curso. Ahora eres un optimizador de código afilado C certificado. Te he mostrado cerca de 25 tipos diferentes de optimización de llamadas. Cómo usarlos en tu propia planta de abrigo, qué mejoras específicas de rendimiento te darán. El optimizamiento se repartieron en tres secciones. Teníamos la optimización básica, la fruta baja colgando donde una sencilla edición fuera de tu abrigo podría resultar en un masivo 100 pliegues Mejora del desempeño. . Tenemos las organizaciones intermedias que fueron más complejas, denominadas los derechos que ofrecen sólo un pequeño a mediano tamaño mejoras en. Tuvimos al núcleo duro optimización avanzada donde usamos varias características exóticas fuera del marco Net para impulsar el rendimiento. Esta optimización se te ha dado una rica caja de herramientas fuera de conocimientos e ideas que puedes usar al escribir tu propio abrigo o cuando estás colaborando en un equipo de desarrollo, sobre todo si estás trabajando en códigos de misión crítica donde el máximo rendimiento es cruciales. Y si descubriste alguna interesante nueva técnica de performance de tu propia policía, compártalos en la discusión del curso para él, para que todos disfrutemos. Espero que disfrutes del curso sobre aprendido algunas técnicas nuevas útiles que puedes aplicar en tu carrera de desarrollos de software Ahora ve y construye grandes cosas