GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-121313: Limit the reading size from pipes to their default buffer size on Unix systems by aplaikner · Pull Request #121315 · python/cpython

Conversation

@aplaikner

@aplaikner aplaikner commented Jul 3, 2024 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Issue: #121313

  • Issue: Limit the reading size from pipes to their default buffer size on Unix systems #121313

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@aplaikner
Add clean code
108e65b
@ghost

ghost commented Jul 3, 2024 •
edited by ghost
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.
CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app

bedevere-app Bot commented Jul 3, 2024

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot mentioned this pull request Jul 3, 2024
Limit the reading size from pipes to their default buffer size on Unix systems #121313
Closed
@bedevere-app bedevere-app Bot added the awaiting review label Jul 3, 2024
@aplaikner
Fix linting error & sysconf unsupported error
37ca606
@bedevere-app

bedevere-app Bot commented Jul 3, 2024

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner
Merge branch 'main' into feature-smaller-pipe-buffer-pull-request
df4f307
@bedevere-app

bedevere-app Bot commented Jul 3, 2024

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

blurb-it Bot and others added 6 commits July 3, 2024 10:11
@blurb-it
📜🤖 Added by blurb_it.
89936c2
@aplaikner
Fix news
31c65b4
@aplaikner
Make page size non static
0a4e4a3
@aplaikner
Merge branch 'main' into feature-smaller-pipe-buffer-pull-request
7afde51
@aplaikner
Merge branch 'main' into feature-smaller-pipe-buffer-pull-request
8d9b16e
@aplaikner
Remove redundant call to pymin
e6c64fe
@cmaloney

cmaloney commented Jul 4, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

os.read() / _os_read_impl is used for reading from most kinds of files in Python. Definitely the limited size makes sense for pipes, but disk I/O generally wants "as big a read as possible". For instance reading regular files, such as python source code, one read call with a buffer that can fit the whole file is fastest in my experimenting. For both that case and the pipe case, it would be more efficient to figure out "whats the max read size" once (with the system calls that entails potentially) and re-use that for every subsequent read call

Following your chain of pieces, could this be made to be more targeted to the specific case potentially? Two thoughts

  1. This is specifically caused by Lib/multiprocessing/connection.py, can that specify explicitly the size of read it wants?
  2. Rather than checking / adjusting the size for every read, could that be done just when the pipe is opened/created? So on open, check type, and stash the "max read size". Compare against that (The code currently checks against _PY_READ_MAX constant, this would just be saying max read size is file type dependent, which is true on both Windows and Linux)

See also: gh-117151 which is aiming to increase the default size (albeit focused around write performance)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner

aplaikner commented Jul 5, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

I've tried shifting the check to Lib/multiprocessing/connection.py and it seems promising, yielding the same performance improvements as having the checks in the C code. The change to os_read_impl would be reverted and the following patch applied to Lib/multiprocessing/connection.py:

diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index b7e1e13217..4797ca4df8 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -18,6 +18,7 @@
 import time
 import tempfile
 import itertools
+import stat
 
 
 from . import util
@@ -391,8 +392,17 @@ def _recv(self, size, read=_read):
         buf = io.BytesIO()
         handle = self._handle
         remaining = size
+        is_pipe = False
+        page_size = 0
+        if not _winapi:
+            page_size = os.sysconf(os.sysconf_names['SC_PAGESIZE'])
+            if size > 16 * page_size:
+                mode = os.fstat(handle).st_mode
+                is_pipe = stat.S_ISFIFO(mode)
+        limit = 16 * page_size if is_pipe else remaining
         while remaining > 0:
-            chunk = read(handle, remaining)
+            to_read = min(limit, remaining)
+            chunk = read(handle, to_read)
             n = len(chunk)
             if n == 0:
                 if remaining == size:
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner
Shift pipe check to connection.py _recv
936b601
@aplaikner
aplaikner requested a review from gpshead as a code owner July 5, 2024 07:43
aplaikner added 2 commits July 5, 2024 13:04
@aplaikner
Make pipe size dependant on systems page size
49d8adb
@aplaikner
Only execute fstat in case reading size is bigger than default pipe size
43e19dd
cmaloney
cmaloney reviewed Jul 5, 2024
View reviewed changes

@cmaloney cmaloney left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking reasonable to me overall: Unlikely to break compatibility or reduce performance, improves default behavior. A couple smaller change requests from me.

It would be nice to add a test that will fail if something breaks / results in the "read too large on pipes resulting in bad behavior" again, although I don't see a straightforward way to do that (Maybe mocking Connection._read in a new test in _test_multiprocessing and checking the size of read when know it is a pipe?)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/multiprocessing/connection.py Outdated
import time
import tempfile
import itertools
import stat

@cmaloney cmaloney Jul 5, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal nitpick, PEP-8 doesn't seem to specify (https://peps.python.org/pep-0008/#imports), but I like imports to be alphabetical. itertools, time, and tempfile which were already in the code just above this are also out of order (although time and tempfile only slightly). Rest are in order. Not sure if it matters for Python core developer acceptance

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aplaikner aplaikner Jul 7, 2024

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Lib/multiprocessing/connection.py Outdated
is_pipe = False
page_size = 0
if not _winapi:
page_size = os.sysconf(os.sysconf_names['SC_PAGESIZE'])

@cmaloney cmaloney Jul 5, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than do the if not _winapi here, which has to be run/interpreted per _recv call, can you add the "calculate max size for a fifo" like https://github.com/python/cpython/blob/main/Lib/multiprocessing/connection.py#L370-L379 does to choose/define the standard read function? Code here will still need to do the min logic + "is this a fifo", but at least reduces overhead work a little bit further.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aplaikner aplaikner Jul 7, 2024

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've shifted fetching the base page size and calculating the default pipe size to the existing if _winapi block above. Is this what you meant?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@cmaloney cmaloney Jul 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, looking good

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Misc/NEWS.d/next/C API/2024-07-03-10-11-53.gh-issue-121313.D7gARW.rst Outdated
@@ -0,0 +1 @@
Limit reading size in os.read for pipes to default pipe size in order to avoid memory overallocation

@cmaloney cmaloney Jul 5, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be updated from os.read -> multiprocessing to follow the logic location change.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aplaikner aplaikner Jul 7, 2024

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@cmaloney cmaloney mentioned this pull request Jul 6, 2024
GH-120754: Add a strace helper and test set of syscalls for open().read() #121143
Merged
5 tasks done
aplaikner and others added 5 commits July 7, 2024 07:17
@aplaikner
Update news message
b0b86e5
@aplaikner
Make imports order alphabetical
2b6ff24
@aplaikner
Shift calculation for pipe size to existing if _winapi check
e726f51
@aplaikner
Fix linting error
59cff4d
@aplaikner
Merge branch 'main' into feature-smaller-pipe-buffer-pull-request
da10f8e
TalAmuyal
TalAmuyal reviewed Jul 7, 2024
View reviewed changes
Comment thread Lib/multiprocessing/connection.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner
Create constant for default number of pages per pipe
94d4c4a
@cmaloney

cmaloney commented Jul 7, 2024

Copy link
Copy Markdown
Contributor

I think as far as I can review / needs a python core dev / someone with more project familiarity to look for high level things.

Some lingering thoughts I have:

  1. Would it make more sense to use fcntl F_GETPIPE_SZ rather than caluclate? I hadn't known about that until reading through the pipe man page linked.
  2. How does this work for non-linux systems? Particularly FreeBSD and Apple systems that are Python supported (https://peps.python.org/pep-0011/#support-tiers). I'm not familiar with pipes on those platforms at all currently.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner

aplaikner commented Jul 7, 2024

Copy link
Copy Markdown
Contributor Author
  1. When using fcntl, an additional system call per _recv would be necessary. The main issue is that the code must be executed inside the _recv function because fcntl requires the pipe's file descriptor. To avoid errors, a check to determine if the system is Windows would be needed before executing fcntl. This could be done with a boolean set inside the if _winapi check. Additionally, there should be a check to verify if the file descriptor belongs to a pipe before attempting to fetch the pipe size. This results in two checks before obtaining the pipe size.
    To optimize performance, these checks could be wrapped in another condition to verify if the read size is smaller than the default pipe size, skipping that code. Otherwise at least the fstat system call would be executed. However, this would again lead to a hardcoded value.
    Using fcntl would provide a more dynamic approach, it would come at the cost of reduced performance due to the additional system calls and other checks, reducing performance.
    I think the current solution covers most cases, where the default pipe size is used. If someone changes that value, they would also need to change the new constant to see some performance benefits.
  2. I'm also not familiar with pipes on those systems, but it seems that FreeBSD and MacOS have both a default pipe buffer size of 64KiB: https://www.netmeister.org/blog/ipcbufs.html
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner

aplaikner commented Jul 29, 2024

Copy link
Copy Markdown
Contributor Author

Hi @cmaloney, I wanted to check in and see if there are any additional steps I need to take for this pull request before it can be reviewed by a core developer.

Thank you!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aplaikner
Merge branch 'main' into feature-smaller-pipe-buffer-pull-request
52606d1
@cmaloney

cmaloney commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

Re: Core Review, as far as I know no other steps needed. From https://devguide.python.org/getting-started/pull-request-lifecycle/#reviewing it's mainly just patience, that document suggests a month wait before pinging other locations.

aplaikner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cmaloney
cmaloney approved these changes Aug 2, 2024
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting core review and removed awaiting review labels Aug 2, 2024
@gpshead gpshead self-assigned this Aug 31, 2024
@gpshead gpshead added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Aug 31, 2024
@bedevere-bot

bedevere-bot commented Aug 31, 2024

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @gpshead for commit 52606d1 🤖

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Aug 31, 2024
@gpshead

gpshead commented Aug 31, 2024

Copy link
Copy Markdown
Member

There's one potential further optimization, at least on Linux. fcntl F_GETPIPE_SZ on the fd if it is a pipe should return the actual size. A pipe might have been configured differently than the platform default. Regardless I don't expect that will have been the case within this multiprocessing code. Using that (and F_SETPIPE_SZ) could be a future enhancement (assuming it proves useful).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gpshead
gpshead merged commit 74bfb53 into python:main Aug 31, 2024
@bedevere-app bedevere-app Bot removed the awaiting core review label Aug 31, 2024
@gpshead

gpshead commented Aug 31, 2024

Copy link
Copy Markdown
Member

Thanks for taking this on!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@methane

methane commented Aug 31, 2024

Copy link
Copy Markdown
Member

2. I'm also not familiar with pipes on those systems, but it seems that FreeBSD and MacOS have both a default pipe buffer size of 64KiB: https://www.netmeister.org/blog/ipcbufs.html

This PR uses 256KiB, not 64KiB on M1 mac (16K page).

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@methane methane mentioned this pull request Sep 1, 2024
gh-121313: multiprocessing: change connection buffer size to 64KiB #123559
Merged
@vstinner

vstinner commented Sep 2, 2024

Copy link
Copy Markdown
Member

The Changelog entry was added to C API category, instead of the Library category.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@methane

methane commented Sep 2, 2024

Copy link
Copy Markdown
Member

Nice catch. I will change the category in #123559.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@methane methane methane left review comments
@devdanzin devdanzin devdanzin left review comments
@cmaloney cmaloney cmaloney approved these changes
@gpshead gpshead Awaiting requested review from gpshead gpshead is a code owner
+1 more reviewer
@TalAmuyal TalAmuyal TalAmuyal left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@gpshead gpshead

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@aplaikner @cmaloney @bedevere-bot @gpshead @methane @vstinner @TalAmuyal @devdanzin
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.