python 和 gobject 的图像加载性能问题

发布于 2024-11-04 08:47:48 字数 838 浏览 0 评论 0原文

我有一个带有 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 毫秒花在了什么上?

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

然后是 Pixbuf 加载:

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

平均需要 1400 毫秒左右,这比让 Gtk 完成这一切要长近 3 倍。

我在这里做错了什么吗?

I have a script with a GTK(GObject) interface I use for posting to my photo blog.

I'm trying to improve it's responsiveness by loading the images in a background thread.

I've had no luck trying to populate GdkPixbuf objects from a background thread, everything I've tried just jams solid.

So as an alternate I thought I'd read the files in the background thread and then push them into GdkPixbuf's on demand. This approach has yielded some surprising and rather depressing performance results which make me wonder if I'm doing something grossly wrong.

I'm playing with lightly compressed jpegs off my camera, they tend to be around 3.8mb.

Here's the original blocking image load:

pb = GdkPixbuf.Pixbuf.new_from_file(image_file)

This averages about 550ms, not huge, but rather tedious if you want to flick through a dozen images.

Then I split it up, here's the file read:

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

This averages 15ms, that's really nice, but also kinda worrying, if we can read the file in 15ms what are the other 535ms being spent on?

Incidentally the bytearray call exists because the PixBufLoader wouldn't accept the data otherwise.

And then the Pixbuf load:

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

This averages around 1400ms, which is nearly 3 times longer than letting Gtk do it all.

Am I doing something wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

毁虫ゝ 2024-11-11 08:47:48

我的猜测:你做错了什么。我刚刚将 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

GdkPixbuf.PixbufLoader使用 gi.repository 确实比“纯”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

但是即使使用gi.repositoryGdkPixbuf.Pixbuf.new_from_file的工作速度也与纯C版本一样快,所以你仍然要么做错了什么,要么期望太多。

My guess: you are doing something wrong. I've just compared libjpeg-turbo with gdk.PixbufLoader and found virtually no speed differences. The code I used is below.

For the 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);
}

For 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)

Test run results:

$ 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

EDIT: and another test, using gi.repostiroy this time:

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)

And results:

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

GdkPixbuf.PixbufLoader using gi.repository is really much MUCH slower then "pure" gtk.gdk. Code:

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)

Results:

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

But GdkPixbuf.Pixbuf.new_from_file works as fast as pure C version even using gi.repository, so you are still either doing something wrong, or expecting too much.

木森分化 2024-11-11 08:47:48

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

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

I have developed a small image viewer with pygtk. I use PixbufLoader, but I feed only N bytes per write(). In combination with idle_add() I can load an image in background, while the application still responses to user input.

Here is the source: http://guettli.sourceforge.net/gthumpy/src/ImageCache.py

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文