2009-11-23 6 views
6

En la aplicación que estoy desarrollando, usamos el control DevExpress XtraGrid, que tiene un evento RowCellStyle que permite personalizar el estilo de cada celda. Los controladores de eventos para este evento por lo general se ven como que:Costo de creación de objetos de fuente en .NET

private gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e) 
{ 
    if (/* Some condition */) 
    { 
     e.Appearance.Font = new Font(gridView1.Appearance.Font, FontStyle.Bold); 
    } 
} 

Este controlador se llama cada vez que una célula se vuelve, por lo que puede crear un gran número de casos Font. Así que me pregunto sobre el costo de hacer eso ... Hice algunos experimentos, y parece que se crea un nuevo identificador HFONT cada vez. ¿Debería preocuparme por eso? ¿Qué tan grande es el impacto en el uso de los recursos?

Si tiene un impacto significativo en el rendimiento, ¿no debería haber una clase FontCache o algo similar?

Nota: Sé cómo resolver el problema (sólo hay que crear la fuente de una vez y volver a utilizar cada vez), mi pregunta es realmente sobre el coste de la creación de muchas HFONT maneja

+1

Accidentalmente, tuve un código como ese en un juego render-loop una vez. Creo que bajó el FPS de> 200 a menos de 40. – Jimmy

+0

La optimización prematura es la raíz de todo el mal -____-. –

+6

El despido prematuro de las preocupaciones sobre el rendimiento es un mal menos celebrado. – Jimmy

Respuesta

6

Prueba de ello; Tengo alrededor de doble actuación de reutilización (de liberación, reutilización = 3000 ms, recrear = 4900ms)

using System.Windows.Forms; 
using System.Drawing; 
using System.Diagnostics; 
static class Program 
{ 
    static void Main() 
    { 
     Button btn1, btn2; 
     Form form = new Form 
     { 
      Controls = { 
       (btn1 = new Button { Dock = DockStyle.Bottom, Text = "reuse" }), 
       (btn2 = new Button { Dock = DockStyle.Bottom, Text = "recreate"}) 
      } 
     }; 
     btn1.Click += delegate 
     { 
      var watch = Stopwatch.StartNew(); 
      using (var gfx = form.CreateGraphics()) 
      using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold)) 
      {     
       gfx.Clear(SystemColors.Control); 
       for (int i = 0; i < 10000; i++) 
       { 
        gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152); 
       } 
      } 
      watch.Stop(); 
      form.Text = watch.ElapsedMilliseconds + "ms"; 
     }; 
     btn2.Click += delegate 
     { 
      var watch = Stopwatch.StartNew(); 
      using (var gfx = form.CreateGraphics()) 
      { 
       gfx.Clear(SystemColors.Control); 
       for (int i = 0; i < 10000; i++) 
       { 
        using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold)) 
        { 
         gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152); 
        } 
       } 
      } 
      watch.Stop(); 
      form.Text = watch.ElapsedMilliseconds + "ms"; 
     }; 
     Application.Run(form); 

    } 
} 
+1

Argumento bastante convincente ... ¡gracias! ¿Hay algún mecanismo de caché de fuentes existente en .NET? No pude encontrar nada como eso en System.Drawing ... –

2

fuente implementa IDisposable - usted debe asegurarse de que llame Desechar cuando haya terminado con él.

Es un recurso no administrado por lo que es posible que el sistema operativo eventualmente se quede sin recursos para brindarle más objetos.

Compruebe el número de identificadores de GDI en el Administrador de tareas para ver si aumenta continuamente.

+0

Además, si vas a utilizar mucho esa fuente, ¡hazla una vez y vuelve a utilizarla! No hay necesidad de instanciarlo continuamente. –

+0

@Sonny Boy - vea la pregunta:> Nota: Sé cómo resolver el problema (solo tengo que crear la fuente una vez y reutilizarla siempre), mi pregunta es realmente sobre el costo de crear muchos identificadores HFONT –

2

La reutilización es casi siempre más rápida que a pedido, pero su capacidad para reutilizar o almacenar en caché objetos de fuentes puede variar.

Si está reutilizando objetos Font y asociando distintos objetos Font con cada control, aumentará las posibilidades de sobrepasar el límite del controlador gdi. Esto dañará todas las aplicaciones en uso por un usuario dado cuando ocurra.

Idealmente, debe almacenar en caché objetos de fuentes a nivel de aplicación/proceso y solo el conjunto mínimo necesario (tal vez una docena como máximo). Además, el almacenamiento en caché de un pequeño conjunto compartido de fuentes y otros objetos GDI le permite hacer un mejor trabajo al manejar mensajes difíciles como WM_SETTINGCHANGE. En su ejemplo, está modificando los valores predeterminados, y puede encontrar errores de dibujo cuando un usuario cambia los esquemas o la fuente de visualización predeterminada en Windows. Durante WM_SETTINGCHANGE, consideraría liberar sus copias en caché e inicializar o preparar nuevos conjuntos de objetos de fuentes.

WM_SETTINGCHANGE (Windows) @ MSDN

Si usted tiene la necesidad de tener cientos de diversas fuentes (u otros objetos GDI) disponibles, debería fuentes no caché y sólo crear lo que necesita en la demanda. Esto no es favorable para el rendimiento, pero ayudará a que su aplicación coexista con otras aplicaciones que un usuario pueda tener abiertas. Mantener cientos de objetos GDI aumentará en gran medida la probabilidad de que un usuario encuentre el límite del identificador GDI. Sin embargo, una caché basada en recuento de referencia puede proporcionar una compensación aquí que funcione mejor para usted, con copias impresas de solo las variantes más frecuentemente utilizadas.

Finalmente, como menciona Matt Breckon, utilice IDisposable para todos los objetos GDI que lo tienen disponible. Esto te ayudará a evitar las fugas de la manija de gdi.

+0

+1 , buen punto acerca de WM_SETTINGCHANGE si implemento un mecanismo de caché ... –