Charles-Francois Natali <neolo...@free.fr> added the comment:

Alright, the current behaviour is quite strange:
we don't call msync() when closing the object, we just unmap() it:
mmap_close_method(mmap_object *self, PyObject *unused)
{
[...]
#ifdef UNIX
        if (0 <= self->fd)
                (void) close(self->fd);
        self->fd = -1;
        if (self->data != NULL) {
                munmap(self->data, self->size);
                self->data = NULL;
        }
#endif
[...]
}

But we set self->data to NULL to avoid calling munmap() a second time when 
deallocating the object:
static void
mmap_object_dealloc(mmap_object *m_obj)
{
[ ... ]
#ifdef UNIX
        if (m_obj->fd >= 0)
                (void) close(m_obj->fd);
        if (m_obj->data!=NULL) {
                msync(m_obj->data, m_obj->size, MS_SYNC);
                munmap(m_obj->data, m_obj->size);
        }
#endif /* UNIX */
[ ...]
}

So, if the object has been closed properly before being deallocated, msync() is 
_not_ called.
But, if we don't close the object, then msync() is called.

The attached test script shows the _huge_ performance impact of msync:
when only close() is called (no msync()):
$ ./python /home/cf/test_mmap.py
0.35829615593

when both flush() and close() are called (msync() called):
$ ./python /home/cf/test_mmap.py
4.95999493599

when neither is called, relying on the deallocation (msync() called):
$ ./python /home/cf/test_mmap.py
4.8811671257

And a strace leaves no doubt (called 10 times in a loop) :
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 
0xb80b1000 <0.000019>
write(1, "4.12167286873\n"..., 144.12167286873
)      = 14 <0.000012>
close(3)                                = 0 <0.000010>
munmap(0xb80b2000, 4096)                = 0 <0.000023>
rt_sigaction(SIGINT, {SIG_DFL}, {0x811d630, [], 0}, 8) = 0 <0.000011>
close(5)                                = 0 <0.004889>
msync(0xb69f9000, 10000000, MS_SYNC)    = 0 <0.584054>
munmap(0xb69f9000, 10000000)            = 0 <0.000433>

See how expensive msync() is, and this is just for a 10MB file.

So the attached patch (mmap_msync.diff) removes the call to msync from 
mmap_object_dealloc(). Since UnmapViewOfFile() is only called inside flush() 
method, nothing to remove for MS Windows.

Here's the result of the same test script with the patch:
when only close() is called (no msync()):
$ ./python /home/cf/test_mmap.py
0.370584011078

when both flush() and close() are called (msync() called):
$ ./python /home/cf/test_mmap.py
4.97467517853

when neither is called, relying on the deallocation (msync() not called):
$ ./python /home/cf/test_mmap.py
0.390102148056

So we only get msync() latency when the user explicitely calls flush().

----------
Added file: http://bugs.python.org/file16827/mmap_msync.diff

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue2643>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to