Skip to content

Expose new copy_file_range() syscall in os module. #71013

Closed

Description

@StyXman
mannequin
BPO 26826
Nosy @facundobatista, @ncoghlan, @vstinner, @giampaolo, @encukou, @vadmium, @desbma, @pablogsal
PRs
  • bpo-26826: Expose copy_file_range in the os module #7255
  • Files
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • copy_file_range.diff
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2020-04-22.16:15:30.897>
    created_at = <Date 2016-04-22.11:40:39.381>
    labels = ['type-feature', 'library']
    title = 'Expose new copy_file_range() syscall in os module.'
    updated_at = <Date 2020-04-22.16:15:30.896>
    user = 'https://bugs.python.org/StyXman'

    bugs.python.org fields:

    activity = <Date 2020-04-22.16:15:30.896>
    actor = 'benjamin.peterson'
    assignee = 'none'
    closed = True
    closed_date = <Date 2020-04-22.16:15:30.897>
    closer = 'benjamin.peterson'
    components = ['Library (Lib)']
    creation = <Date 2016-04-22.11:40:39.381>
    creator = 'StyXman'
    dependencies = []
    files = ['42619', '42628', '42638', '42640', '42651', '42708', '42829', '43310', '43318', '43319', '43624', '43639', '43640', '43688']
    hgrepos = []
    issue_num = 26826
    keywords = ['patch']
    message_count = 54.0
    messages = ['264000', '264001', '264004', '264013', '264067', '264071', '264072', '264087', '264166', '264167', '264302', '264304', '264320', '264322', '264336', '264362', '264365', '264371', '264429', '264430', '264433', '264457', '264494', '264502', '264537', '264797', '265121', '265405', '265441', '267899', '268011', '268015', '268019', '269810', '269825', '269881', '269882', '270171', '274746', '317365', '317366', '317667', '317679', '337694', '344100', '344576', '344580', '344582', '344584', '344586', '344602', '344630', '344649', '344672']
    nosy_count = 10.0
    nosy_names = ['facundobatista', 'ncoghlan', 'vstinner', 'giampaolo.rodola', 'StyXman', 'petr.viktorin', 'neologix', 'martin.panter', 'desbma', 'pablogsal']
    pr_nums = ['7255']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue26826'
    versions = ['Python 3.6']

    Activity

    1. StyXman commented on Apr 22, 2016

      StyXmanmannequin
      MannequinAuthor

      copy_file_range() has been introduced in the Linux kernel since version 4.5 (mid march 2016). This new syscall allows to copy data from one fd to another without passing by user space, improving speed in most cases. You can read more about it here:

      https://lwn.net/Articles/659523/

      I intend to start working on adding a binding for it in the os module and then, if it's available, use it in shutils.copy() to improve its efficiency. I have a couple of questions: If the syscall is not available, should I implement a user space alternative or should the method not exist at all?

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Apr 22, 2016
    3. tiran commented on Apr 22, 2016

      @tiran
      Member

      Thanks, looks interesting.

      We usually wait until syscalls are generally available in common distros and have bindings in glibc. It makes it easier to test the feature.

    4. vstinner commented on Apr 22, 2016

      @vstinner
      Member

      We usually wait until syscalls are generally available in common distros and have bindings in glibc. It makes it easier to test the feature.

      Usually, yeah. os.urandom() uses syscall() to use the new getrandom() of Linux since it's still not exposed in the GNU libc...
      https://sourceware.org/bugzilla/show_bug.cgi?id=17252
      Status: NEW

    5. vadmium commented on Apr 22, 2016

      @vadmium
      Member

      Tangentially related: bpo-25156, about using sendfile() to copy files in shutil.

    6. StyXman commented on Apr 23, 2016

      StyXmanmannequin
      MannequinAuthor

      Debian Sid, arch Linux and Fedora FC24 (alpha) already have linux-4.5, others would certainly follow soon. Meanwhile, I could start developing the patch and we could review it when you think it's appropriate.

    7. vstinner commented on Apr 23, 2016

      @vstinner
      Member

      Kerbel support ok, but what about the libc support? Do you know if it is
      planned? Or do you want to use the syscall() low-level API. If we take the
      syscall() path, I suggest to make the function private in the os module.
      Wait until the API is standardized in the libc. Sometimes the libc changes
      minor things, ir's not always a thin wrapper to the syscall.

    8. StyXman commented on Apr 23, 2016

      StyXmanmannequin
      MannequinAuthor
    9. vstinner commented on Apr 24, 2016

      @vstinner
      Member

      That's the manual page of the Linux kernel, not the glibc. It doesn't
      mean that the glibc implemented it.

    10. StyXman commented on Apr 25, 2016

      StyXmanmannequin
      MannequinAuthor

      Then I don't know. My only worry is whether having the method local to os would allow shutils.copy() to use it or not.

      I will start by looking at other similar functions there to see to do it. urandom() looks like a good starting point.

    11. vstinner commented on Apr 25, 2016

      @vstinner
      Member

      If we add a new private function to the os module (ex:
      os._copy_file_range), it can be used in shutil if available. But it
      would be a temporary solution until we declare the API stable. Maybe
      it's ok to add the API as public today.

    12. StyXman commented on Apr 26, 2016

      StyXmanmannequin
      MannequinAuthor

      Ok, I have a preliminary version of the patch. It has several parts:

      • Adding the functionality to the os module, with docstring.
      • Make shutil.copyfileobj() to use it if available.
      • Modify the docs (this has to be done by hand, right?).
      • Modify NEWS and ACKS.

      Several points:

      • For the time being, flags must be 0, so I was not sure whether put the argument or not. Just in case, I put it.

      • I'm not sure how to test for availability, so configure defines HAVE_COPY_FILE_RANGE.

      • No tests yet.

      Talking about tests, I tried copying a 325MiB on an SSD, f2fs. Here are the times:

      Old user space copy:

      $ time ./python -m timeit -n 10 -s 'import shutil' 'a = open ("a.mp4", "rb"); b = open ("b.mp4", "wb+"); shutil.copyfileobj (a, b, 16*1024*1024)'
      10 loops, best of 3: 259 msec per loop
      real    0m7.915s
      user    0m0.104s
      sys     0m7.792s

      New copy_file_range:

      $ time ./python -m timeit -n 10 -s 'import shutil' 'a = open ("a.mp4", "rb"); b = open ("b.mp4", "wb+"); shutil.copyfileobj (a, b, 16*1024*1024)'
      10 loops, best of 3: 193 msec per loop
      real    0m5.926s
      user    0m0.080s
      sys     0m5.836s

      Some 20% improvement, but notice that the buffer size is 1024 times Python's default size (16MiB vs. 16KiB).

      One difference that I notice in semantics is that if the file is not open in binary form, but the file is binary, you get no UnicodeDecodeError (because the data never reaches userspace).

      Let me know what you think.

    13. StyXman commented on Apr 26, 2016

      StyXmanmannequin
      MannequinAuthor

      Version without the NEWS and ACKS change.

    14. StyXman commented on Apr 26, 2016

      StyXmanmannequin
      MannequinAuthor

      Hmm, I just noticed that it doesn't fallback to normal copy if the arguments are not valid (EBADF, EXDEV). Back to the drawing board...

    15. 30 remaining items

    16. StyXman commented on May 25, 2018

      StyXmanmannequin
      MannequinAuthor

      I'm really sorry, but I don't have time to continue with this (new daughter!). Can someone else pick it up?

    17. giampaolo commented on May 25, 2018

      @giampaolo
      Contributor

      Check for availability in configure.ac appears to be broken.

    18. giampaolo commented on Mar 11, 2019

      @giampaolo
      Contributor

      Little update about this. According to:
      http://man7.org/linux/man-pages/man2/copy_file_range.2.html
      ...it seems glibc 2.27 released on 2018-02-01 includes copy_file_range(). I'm not the best candidate for giving advice on C syscalls definitions/availability, but FWIW this works on my Linux 4.15:

      #if defined(__linux__) && \
          defined(SYS_copy_file_range) && \
          defined(__GLIBC_PREREQ) && \
          __GLIBC_PREREQ(2, 27)
      ...
      #endif

      I think (but not sure) this is supposed to fix this condition, which appears to be the major blocker here:
      https://github.com/python/cpython/blob/9a177061cd7190eabf40efd31e8981e0bccd5dc4/Lib/test/test_os.py#L258-L261

    19. pablogsal commented on May 31, 2019

      @pablogsal
      Member

      New changeset aac4d03 by Pablo Galindo in branch 'master':
      bpo-26826: Expose copy_file_range in the os module (GH-7255)
      aac4d03

    20. vstinner commented on Jun 4, 2019

      @vstinner
      Member

      shutil copy functions would definitively benefit of using copy_file_range() if available. Can someone please open a separated issue for shutil?

    21. giampaolo commented on Jun 4, 2019

      @giampaolo
      Contributor

      shutil.copyfile() already uses sendfile() which basically provides the same performances. sendfile() should be preferred though because it’s supported since Linux 2.6.33.

    22. pablogsal commented on Jun 4, 2019

      @pablogsal
      Member

      But copy_file_rane can leverage more filesystem features like deduplication and copy offload stuff.

    23. vstinner commented on Jun 4, 2019

      @vstinner
      Member

      Giampaolo Rodola':

      shutil.copyfile() already uses sendfile() which basically provides the same performances. sendfile() should be preferred though because it’s supported since Linux 2.6.33.

      Pablo Galindo Salgado:

      But copy_file_rane can leverage more filesystem features like deduplication and copy offload stuff.

      We can use copy_file_range() if available, or fallback to sendfile().

    24. giampaolo commented on Jun 4, 2019

      @giampaolo
      Contributor

      I think data deduplication / CoW / reflink copy is better implemented via FICLONE. "cp --reflink" uses it, I presume because it's older than copy_file_range(). I have a working patch adding CoW copy support for Linux and OSX (but not Windows). I think that should be exposed as a separate shutil.reflink() though, and copyfile() should just do a standard copy.

    25. giampaolo commented on Jun 4, 2019

      @giampaolo
      Contributor

      Actually "man copy_file_range" claims it can do server-side copy, meaning no network traffic between client and server if *src* and *dst* live on the same network fs. So I agree copy_file_range() should be preferred over sendfile() after all. =)
      I have a wrapper for copy_file_range() similar to what I did in shutil in bpo-33671 which I can easily integrate, but I wanted to land this one first:
      https://bugs.python.org/issue37096
      Also, I suppose we cannot land this in time for 3.8?

    26. vstinner commented on Jun 4, 2019

      @vstinner
      Member

      Please open a new issue to discuss how it can used in shutil ;-)

    27. vstinner commented on Jun 4, 2019

      @vstinner
      Member

      I created bpo-37157: "shutil: add reflink=False to file copy functions to control clone/CoW copies (use copy_file_range)".

      --

      The new os.copy_file_range() should be documented at:
      https://docs.python.org/dev/whatsnew/3.8.html#os

    28. giampaolo commented on Jun 5, 2019

      @giampaolo
      Contributor

      Please open a new issue to discuss how it can used in shutil ;-)

      Use copy_file_range() in shutil.copyfile():
      https://bugs.python.org/issue37159

    29. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      stdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions