2011-04-30 8 views
7

Tengo un script con una interfaz GTK (GObject) que uso para publicar en mi blog de fotos.imagen cargando problemas de rendimiento con python y gobject

Estoy tratando de mejorar su capacidad de respuesta cargando las imágenes en un hilo de fondo.

No he tenido suerte tratando de rellenar objetos GdkPixbuf a partir de un hilo de fondo, todo lo que he intentado solo se atasca.

Así que como alternativa, pensé que leería los archivos en el hilo de fondo y luego los insertaría en GdkPixbuf a pedido. Este enfoque ha producido algunos resultados de rendimiento sorprendentes y más bien deprimentes que me preguntan si estoy haciendo algo groseramente incorrecto.

Estoy jugando con jpegs ligeramente comprimidos fuera de mi cámara, tienden a estar alrededor de los 3.8mb.

Aquí está la carga de la imagen original de bloqueo:

pb = GdkPixbuf.Pixbuf.new_from_file(image_file) 

Esto promedia aproximadamente 550 ms, no es enorme, pero bastante tedioso si quieres hojear una docena de imágenes.

Entonces dividirlo, aquí está el archivo leído:

data = bytearray(open(self.image_file).read()) 

Esto da un promedio de 15 ms, eso es muy bonito, pero también un poco preocupante, si podemos leer el archivo en 15 ms cuáles son las otras 535ms se gastan ¿en?

Por cierto, la llamada bytearray existe porque PixBufLoader no aceptaría los datos de lo contrario.

Y entonces la carga Pixbuf:

pbl = GdkPixbuf.PixbufLoader() 
pbl.write(data, len(data)) 
pbl.close() 
pb = pbl.get_pixbuf() 

Este promedio es alrededor de 1400ms, que es casi 3 veces más que dejar que GTK haga todo.

¿Estoy haciendo algo mal aquí?

+2

Ha considerado el uso de Python herramientas de perfilado para ver dónde se gasta el tiempo? –

+0

la llamada cercana de Pixbufloader es actualmente el 94% de mi tiempo de ejecución, no recibo ningún detalle dentro de esa llamada. –

Respuesta

2

Supongo que estás haciendo algo mal. Acabo de comparar libjpeg-turbo con gdk.PixbufLoader y prácticamente no encontré diferencias de velocidad. El código que utilicé está debajo.

Para el libjpeg-turbo (jpegload.c):

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/time.h> 

#include <jpeglib.h> 

void decompress(FILE* fd) 
{ 
    JSAMPARRAY buffer; 
    int row_stride; 
    struct jpeg_decompress_struct cinfo; 
    struct jpeg_error_mgr jerr; 
    cinfo.err = jpeg_std_error(&jerr); 
    jpeg_create_decompress(&cinfo); 
    jpeg_stdio_src(&cinfo, fd); 
    jpeg_read_header(&cinfo, TRUE); 
    jpeg_start_decompress(&cinfo); 
    row_stride = cinfo.output_width * cinfo.output_components; 
    buffer = (*cinfo.mem->alloc_sarray) 
       ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); 
    while (cinfo.output_scanline < cinfo.output_height) { 
    (void) jpeg_read_scanlines(&cinfo, buffer, 1); 
    } 
    jpeg_finish_decompress(&cinfo); 
    jpeg_destroy_decompress(&cinfo); 
} 

int main(int argc, char** argv) 
{ 
    long len; 
    FILE *fd; 
    unsigned char *buf; 
    struct timeval start, end; 
    int i; 
    const int N = 100; 
    int delta; 

    /* read file to cache it in memory */ 
    assert(argc == 2); 
    fd = fopen(argv[1], "rb"); 
    fseek(fd, 0, SEEK_END); 
    len = ftell(fd); 
    rewind(fd); 
    buf = malloc(len); 
    assert(buf != NULL); 
    assert(fread(buf, 1, len, fd) == len); 

    gettimeofday(&start, NULL); 
    for(i = 0; i < N; i++) { 
    rewind(fd); 
    decompress(fd); 
    } 
    gettimeofday(&end, NULL); 
    if(end.tv_sec > start.tv_sec) { 
    delta = (end.tv_sec - start.tv_sec - 1) * 1000; 
    end.tv_usec += 1000000; 
    } 
    delta += (end.tv_usec - start.tv_usec)/1000; 
    printf("time spent in decompression: %d msec\n", 
     delta/N); 
} 

Para gdk pitón (gdk_load.py):

import sys 
import gtk 
import time 

def decompress(data): 
    pbl = gtk.gdk.PixbufLoader() 
    pbl.write(data, len(data)) 
    pbl.close() 
    return pbl.get_pixbuf() 

data = open(sys.argv[1]).read() 

N = 100 
start = time.time() 
for i in xrange(N): 
    decompress(data) 
end = time.time() 
print "time spent in decompression: %d msec" % int((end - start) * 1000/N) 

resultados de la ejecución de la prueba:

$ gcc jpegload.c -ljpeg 
$ ./a.out DSC_8450.JPG 
time spent in decompression: 75 msec 
$ python gdk_load.py DSC_8450.JPG 
time spent in decompression: 75 msec 
$ identify DSC_8450.JPG 
DSC_8450.JPG JPEG 3008x2000 3008x2000+0+0 8-bit DirectClass 2.626MB 0.000u 0:00.019 

EDITAR : y otra prueba, usando gi.repostiroy esta vez:

import sys 
import time 
from gi.repository import GdkPixbuf 

def decompress(filename): 
    pb = GdkPixbuf.Pixbuf.new_from_file(filename) 
    return pb 

N = 100 
start = time.time() 
for i in xrange(N): 
    decompress(sys.argv[1]) 
end = time.time() 
print "time spent in decompression: %d msec" % int((end - start) * 1000/N) 

y resultados:

$ python gi_load.py DSC_8450.JPG 
time spent in decompression: 74 msec 

GdkPixbuf.PixbufLoader usando gi.repository es realmente mucho, mucho más lento que "puro" gtk.gdk.Código:

import sys 
import time 
from gi.repository import GdkPixbuf 

def decompress(data): 
    pbl = GdkPixbuf.PixbufLoader() 
    pbl.write(data, len(data)) 
    pbl.close() 
    return pbl.get_pixbuf() 

data = bytearray(open(sys.argv[1]).read()) 

N = 100 
start = time.time() 
for i in xrange(N): 
    decompress(data) 
end = time.time() 
print "time spent in decompression: %d msec" % int((end - start) * 1000/N) 

Resultados:

$ python gi_load.py DSC_8450.JPG 
time spent in decompression: 412 msec 

Pero GdkPixbuf.Pixbuf.new_from_file trabaja como rápido como la versión pura C, incluso utilizando gi.repository, por lo que aún sea haciendo algo mal, o esperar demasiado.

+0

En GObject, pasar los datos como leídos del archivo no funcionó, de ahí la necesidad de la llamada de bytearray, me pregunto si ese es el origen del problema. –

+0

¿Qué quiere decir con "bajo GObject"? ¿Estás usando el código de E/S gobject para leer archivos? – abbot

+0

En lugar de importar gtk, estoy importando Gtk desde gi.repository, aparentemente este es el "nuevo GObject", el sitio pygtk lo recomienda para código nuevo, en la práctica significa que todo es sutilmente diferente de los ejemplos de pygtk que encuentro en el web. –

1

He desarrollado un pequeño visor de imágenes con pygtk. Yo uso PixbufLoader, pero solo alimentaré N bytes por escritura(). En combinación con idle_add() puedo cargar una imagen en segundo plano, mientras la aplicación sigue respondiendo a la entrada del usuario.

Aquí está la fuente: http://guettli.sourceforge.net/gthumpy/src/ImageCache.py

+0

Eso es interesante, actualmente experimento un gran retraso (2 segundos) en la llamada cercana del pixbufloader. Desafortunadamente no pude hacer que el tuyo corriera para ver si tiene un problema similar. –

Cuestiones relacionadas