为什么使用 Python mmap 模块比从 C++ 调用 POSIX mmap 慢得多?
Why is using the Python mmap module much slower than calling POSIX mmap from C++?
C++代码:
#include <string>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main() {
timeval tv1, tv2, tv3, tve;
gettimeofday(&tv1, 0);
int size = 0x1000000;
int fd = open("data", O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
ftruncate(fd, size);
char *data = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
for(int i = 0; i < size; i++) {
data[i] = 'S';
}
munmap(data, size);
close(fd);
gettimeofday(&tv2, 0);
timersub(&tv2, &tv1, &tve);
printf("Time elapsed: %ld.%06lds\n", (long int) tve.tv_sec, (long int) tve.tv_usec);
}
Python代码:
import mmap
import time
t1 = time.time()
size = 0x1000000
f = open('data/data', 'w+')
f.truncate(size)
f.close()
file = open('data/data', 'r+b')
buffer = mmap.mmap(file.fileno(), 0)
for i in xrange(size):
buffer[i] = 'S'
buffer.close()
file.close()
t2 = time.time()
print "Time elapsed: %.3fs" % (t2 - t1)
我认为这两个程序本质上是相同的,因为 C++ 和 Python 调用相同的系统调用 (mmap
)。
但是 Python 版本比 C++ 版本慢得多:
Python: Time elapsed: 1.981s
C++: Time elapsed: 0.062143s
谁能解释一下为什么mmap Python比C++慢很多?
环境:
C++:
$ c++ --version
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.5.0
Python:
$ python --version
Python 2.7.11 :: Anaconda 4.0.0 (x86_64)
不是mmap
慢,而是用值填充数组。 Python 众所周知,在执行原始操作时速度很慢。使用更高级别的操作:
buffer[:] = 'S' * size
详细说明@Daniel 所说的——任何 Python 操作的开销(在某些情况下 way 比数量级更多)用 C++ 实现解决方案的代码。
填充缓冲区的循环确实是罪魁祸首——而且 mmap
模块本身比你想象的要多得多,尽管它提供了一个接口,其语义是误导性的,verrrry与 POSIX mmap()
紧密对齐。你知道 POSIX mmap()
是如何扔给你一个 void*
(你只需要使用 munmap()
来清理它,在某些时候)? Python 的 mmap
必须分配一个 PyObject
结构来照顾 void*
– 通过提供元数据和回调使其符合 Python 的缓冲协议运行时,传播和排队读取和写入,维护 GIL 状态,无论发生什么错误都清理其分配......
所有这些东西也需要时间和记忆。我个人从来没有发现自己使用 mmap
模块,因为它在任何 I/O 问题上都没有给你明显的优势,比如开箱即用——你可以使用 mmap
使事情变慢和使事情变快一样容易。
相比之下,我经常*确实*发现使用 POSIX mmap()
在执行 I/O 时非常有利来自 Python C/C++ 扩展(前提是你在意 GIL 状态),正是因为围绕 mmap()
编码避免了所有 Python 首先是内部基础设施。
C++代码:
#include <string>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main() {
timeval tv1, tv2, tv3, tve;
gettimeofday(&tv1, 0);
int size = 0x1000000;
int fd = open("data", O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
ftruncate(fd, size);
char *data = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
for(int i = 0; i < size; i++) {
data[i] = 'S';
}
munmap(data, size);
close(fd);
gettimeofday(&tv2, 0);
timersub(&tv2, &tv1, &tve);
printf("Time elapsed: %ld.%06lds\n", (long int) tve.tv_sec, (long int) tve.tv_usec);
}
Python代码:
import mmap
import time
t1 = time.time()
size = 0x1000000
f = open('data/data', 'w+')
f.truncate(size)
f.close()
file = open('data/data', 'r+b')
buffer = mmap.mmap(file.fileno(), 0)
for i in xrange(size):
buffer[i] = 'S'
buffer.close()
file.close()
t2 = time.time()
print "Time elapsed: %.3fs" % (t2 - t1)
我认为这两个程序本质上是相同的,因为 C++ 和 Python 调用相同的系统调用 (mmap
)。
但是 Python 版本比 C++ 版本慢得多:
Python: Time elapsed: 1.981s
C++: Time elapsed: 0.062143s
谁能解释一下为什么mmap Python比C++慢很多?
环境:
C++:
$ c++ --version
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.5.0
Python:
$ python --version
Python 2.7.11 :: Anaconda 4.0.0 (x86_64)
不是mmap
慢,而是用值填充数组。 Python 众所周知,在执行原始操作时速度很慢。使用更高级别的操作:
buffer[:] = 'S' * size
详细说明@Daniel 所说的——任何 Python 操作的开销(在某些情况下 way 比数量级更多)用 C++ 实现解决方案的代码。
填充缓冲区的循环确实是罪魁祸首——而且 mmap
模块本身比你想象的要多得多,尽管它提供了一个接口,其语义是误导性的,verrrry与 POSIX mmap()
紧密对齐。你知道 POSIX mmap()
是如何扔给你一个 void*
(你只需要使用 munmap()
来清理它,在某些时候)? Python 的 mmap
必须分配一个 PyObject
结构来照顾 void*
– 通过提供元数据和回调使其符合 Python 的缓冲协议运行时,传播和排队读取和写入,维护 GIL 状态,无论发生什么错误都清理其分配......
所有这些东西也需要时间和记忆。我个人从来没有发现自己使用 mmap
模块,因为它在任何 I/O 问题上都没有给你明显的优势,比如开箱即用——你可以使用 mmap
使事情变慢和使事情变快一样容易。
相比之下,我经常*确实*发现使用 POSIX mmap()
在执行 I/O 时非常有利来自 Python C/C++ 扩展(前提是你在意 GIL 状态),正是因为围绕 mmap()
编码避免了所有 Python 首先是内部基础设施。