Skip to content

Instantly share code, notes, and snippets.

@lazka
Last active July 17, 2016 17:18
Show Gist options
  • Save lazka/78bdfdbf8b01f1514f1c801acf321930 to your computer and use it in GitHub Desktop.
Save lazka/78bdfdbf8b01f1514f1c801acf321930 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2016 Christoph Reiter
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
import os
import sys
import time
import ctypes
import platform
import errno
from ctypes import cdll, CDLL, c_long, c_int, c_uint64, POINTER, c_size_t, \
c_uint, byref, c_ssize_t
from mutagen._util import mmap_move
from mutagen._compat import xrange
c_loff_t = c_uint64
def get_copy_file_range_func(_cache=[]):
"""Returns a callable or None"""
if _cache:
return _cache[0]
def ret(value):
_cache.append(value)
return value
if not sys.platform.startswith("linux"):
return ret(None)
try:
lib = CDLL("libc.so.6", use_errno=True)
except OSError:
return ret(None)
syscall = lib.syscall
syscall.argtypes = [
c_long, c_int, POINTER(c_loff_t), c_int, POINTER(c_loff_t),
c_ssize_t, c_uint]
syscall.restype = c_ssize_t
# See https://github.com/systemd/systemd/blob/
# 8869a0b40b1cf82d264cabc2ff8052e8e3/src/basic/missing_syscall.h#L270
syscall_nums = {
"x86_64": 326,
"i386": 377,
"s390": 375,
"arm": 391,
"aarch64": 285,
}
machine = platform.machine()
try:
NR_copy_file_range = syscall_nums[machine]
except KeyError:
return ret(None)
return ret(lambda *args: syscall(NR_copy_file_range, *args))
def copy_file_range(fd_in, off_in, fd_out, off_out, length, flags):
"""
Args:
fd_in (int): File descriptor to read from
off_in (int): Offset to read from or None to use the file offset
fd_out (int): File descriptor to write to
off_out (int): Offset to write to or None to use the file offset
length (int): Amount of bytes to copy
flags (int): always 0
Will either copy everything or raise OSError. See OSError.errno for the
error code.
For more info see ``man 2 copy_file_range``
"""
if off_in is not None:
off_in_v = c_loff_t(off_in)
off_in = byref(off_in_v)
if off_out is not None:
off_out_v = c_loff_t(off_out)
off_out = byref(off_out_v)
syscall = get_copy_file_range_func()
if syscall is None:
raise OSError(errno.ENOSYS, os.strerror(errno.ENOSYS))
i = 0
bytes_copied = -1
while length > 0:
# off_in_v, off_out_v will be adjusted by the syscall
bytes_copied = syscall(fd_in, off_in, fd_out, off_out, length, flags)
if bytes_copied == -1:
err = ctypes.get_errno()
raise OSError(err, os.strerror(err))
length -= bytes_copied
if bytes_copied == 0 and length != 0:
# https://bugzilla.kernel.org/show_bug.cgi?id=135451
if os.fstat(fd_in).st_size == off_in_v.value:
raise OSError(errno.EINVAL, os.strerror(errno.EINVAL))
def main():
h = open("dsafdsafsafdf.txt", "wb+")
for i in xrange(2000000):
h.write(b"abcdefghijklmnop\n")
h.flush()
h.seek(0, 2)
size = h.tell()
fd = h.fileno()
N = 200
os.fsync(fd)
t = time.time()
for i in xrange(N):
mmap_move(h, 1, 0, size - 1)
print(time.time() - t, "mmap")
os.fsync(fd)
t = time.time()
for i in xrange(N):
copy_file_range(fd, 0, fd, 1, size - 1, 0)
print(time.time() - t, "copy_file_range")
h.close()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment