gpt4 book ai didi

python - 为什么 os.lseek() 在类文件对象上比 seek() 慢?

转载 作者:太空宇宙 更新时间:2023-11-03 14:13:15 26 4
gpt4 key购买 nike

在 Python 中,为什么是 os.lseek()seek() 慢很多file-like objects 上的方法?

$ dd if=/dev/urandom of=test.bin bs=1024 count=1024
1024+0 records in
1024+0 records out
1048576 bytes transferred in 0.063247 secs (16579072 bytes/sec)
$ python -m timeit -s 'import os; f = open("test.bin", "r")' 'for i in xrange(10000): f.seek(i, os.SEEK_SET)'
100 loops, best of 3: 2.62 msec per loop
$ python -m timeit -s 'import os; f = os.open("test.bin", os.O_RDONLY)' 'for i in xrange(10000): os.lseek(f, i, os.SEEK_SET)'
100 loops, best of 3: 4.23 msec per loop

docs for os.open()说“此函数用于低级 I/O”。我认为“低级 I/O”会更快。

我在配备固态硬盘的 MacBook Pro 上使用 Mac OS 10.10.5 上的 CPython 2.7.9。

最佳答案

低级别并不一定意味着更快。它只是意味着低级。鉴于 python 主要用于高级用途,因此高级 API 通常经过相当优化,并且避免了您必须处理编写“等效”低级代码的陷阱。

现在 os.open 返回一个 文件描述符,它是一个整数,这是系统调用实际传递的内容(这就是它被称为低级的原因. 你通常不想直接处理文件描述符并将其留给解释器。)

open 函数返回一个file 对象。可以找到seek方法的实现here它非常简单:它会进行一些错误检查,最后调用 _portable_fseek:

Py_DECREF(off_index);
if (PyErr_Occurred())
return NULL;

FILE_BEGIN_ALLOW_THREADS(f)

errno = 0;
ret = _portable_fseek(f->f_fp, offset, whence);
FILE_END_ALLOW_THREADS(f)

if (ret != 0) {
PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
return NULL;
}

f->f_skipnextlf = 0;
Py_INCREF(Py_None);
return Py_None;

定义_portable_fseek的地方here它的实现是 真的只是:

static int
_portable_fseek(FILE *fp, Py_off_t offset, int whence)
{
#if !defined(HAVE_LARGEFILE_SUPPORT)
return fseek(fp, offset, whence);

#elif defined(HAVE_FSEEKO) && SIZEOF_OFF_T >= 8
return fseeko(fp, offset, whence);

#elif defined(HAVE_FSEEK64)
return fseek64(fp, offset, whence);

#elif defined(__BEOS__)
return _fseek(fp, offset, whence);

#elif SIZEOF_FPOS_T >= 8
/* lacking a 64-bit capable fseek(), use a 64-bit capable fsetpos()
and fgetpos() to implement fseek()*/
fpos_t pos;
switch (whence) {
case SEEK_END:
#ifdef MS_WINDOWS
fflush(fp);
if (_lseeki64(fileno(fp), 0, 2) == -1)
return -1;
#else
if (fseek(fp, 0, SEEK_END) != 0)
return -1;
#endif
/* fall through */
case SEEK_CUR:
if (fgetpos(fp, &pos) != 0)
return -1;
offset += pos;
break;
/* case SEEK_SET: break; */
}
return fsetpos(fp, &offset);
#else
#error "Large file support, but no way to fseek."
#endif
}

os.lseek 函数被定义为 here它几乎是相同的代码,除了它是这样做的:

    if (!_PyVerify_fd(fd))
return posix_error();
Py_BEGIN_ALLOW_THREADS
#if defined(MS_WIN64) || defined(MS_WINDOWS)
res = _lseeki64(fd, pos, how);
#else
res = lseek(fd, pos, how);
#endif
Py_END_ALLOW_THREADS

注意对 _PyVerify_fd 的调用!

您可以使用任何 整数对象调用os.lseek,因此解释器必须验证:

  1. 整数在正确范围内
  2. 它引用了一个现有的打开文件描述符

当使用文件对象时,您可以假设与文件对象关联的文件描述符是有效的并避免检查。

因此在这种情况下,低级函数实际上必须执行更多的错误检查,从而使操作变慢。


还有第三种查找文件的方法,即使用io 库。结果是:

$ dd if=/dev/urandom of=test.bin bs=1024 count=1024
1024+0 record dentro
1024+0 record fuori
1048576 byte (1,0 MB) copiati, 0,0851599 s, 12,3 MB/s
$ python2 -m timeit -s 'import io;import os; f=open("test.bin", "r")' 'for i in xrange(10000): f.seek(i, os.SEEK_SET)'
100 loops, best of 3: 5.72 msec per loop
$ python2 -m timeit -s 'import io;import os; f=os.open("test.bin", os.O_RDONLY)' 'for i in xrange(10000): os.lseek(f, i, os.SEEK_SET)'
100 loops, best of 3: 6.28 msec per loop
$ python2 -m timeit -s 'import io;import os; f=io.open("test.bin", "r")' 'for i in xrange(10000): f.seek(i, os.SEEK_SET)'
10 loops, best of 3: 63.8 msec per loop

它们花费的时间是普通文件的 10 倍!但是,如果您查看它们的实现方式 here您会看到它们的实现使用了相当高级的 API,并且与纯 C 版本相比引入了相当多的开销。

另请注意,在我的机器上,os.lseekseek 之间没有 2 倍的差异。

关于python - 为什么 os.lseek() 在类文件对象上比 seek() 慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35528471/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com