7

我有一个带有 GTK(GObject) 界面的脚本,用于发布到我的照片博客。

我试图通过在后台线程中加载图像来提高它的响应能力。

我没有运气尝试从后台线程填充 GdkPixbuf 对象,我尝试过的一切都只是卡住了。

因此,作为替代方案,我想我会在后台线程中读取文件,然后按需将它们推送到 GdkPixbuf 中。这种方法产生了一些令人惊讶且相当令人沮丧的性能结果,这让我怀疑我是否做错了什么。

我正在使用相机上轻微压缩的 jpeg,它们往往在 3.8mb 左右。

这是原始的阻塞图像加载:

pb = GdkPixbuf.Pixbuf.new_from_file(image_file)

这平均约为 550 毫秒,不是很大,但如果您想浏览十几个图像,则相当乏味。

然后我把它分开,这是读取的文件:

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

这平均为 15 毫秒,这非常好,但也有点令人担忧,如果我们可以在 15 毫秒内读取文件,那么其他 535 毫秒会花在什么上面?

顺便说一句,存在 bytearray 调用是因为 PixBufLoader 否则不会接受数据。

然后 Pixbuf 加载:

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

这平均约为 1400 毫秒,比让 Gtk 完成这一切的时间长了近 3 倍。

我在这里做错了吗?

4

2 回答 2

2

我的猜测:你做错了什么。我刚刚将 libjpeg-turbo 与 gdk.PixbufLoader 进行了比较,发现几乎没有速度差异。我使用的代码如下。

对于 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);
}

对于 python gdk (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)

试运行结果:

$ 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

编辑:和另一个测试,使用gi.repostiroy这个时间:

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)

和结果:

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

使用 gi.repository 的 GdkPixbuf.PixbufLoader 确实比“纯”慢得多gtk.gdk。代码:

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)

结果:

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

但是GdkPixbuf.Pixbuf.new_from_file即使使用 ,它的运行速度也与纯 C 版本一样快gi.repository,因此您仍然要么做错了什么,要么期望过高。

于 2011-05-03T12:52:33.220 回答
1

我用 pygtk 开发了一个小型图像查看器。我使用 PixbufLoader,但每次 write() 只输入 N 个字节。结合 idle_add() 我可以在后台加载图像,而应用程序仍然响应用户输入。

这是来源: http: //guettli.sourceforge.net/gthumpy/src/ImageCache.py

于 2011-06-16T14:56:52.000 回答