Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asynchat push always sends 512 bytes (ignoring ac_out_buffer_size) #46345

Closed
mkc mannequin opened this issue Feb 11, 2008 · 12 comments
Closed

asynchat push always sends 512 bytes (ignoring ac_out_buffer_size) #46345

mkc mannequin opened this issue Feb 11, 2008 · 12 comments
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@mkc
Copy link
Mannequin

mkc mannequin commented Feb 11, 2008

BPO 2073
Nosy @akuchling, @josiahcarlson, @giampaolo, @intgr

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 2009-03-31.16:41:43.917>
created_at = <Date 2008-02-11.22:17:31.712>
labels = ['type-bug', 'library']
title = 'asynchat push always sends 512 bytes (ignoring ac_out_buffer_size)'
updated_at = <Date 2009-03-31.21:56:53.595>
user = 'https://bugs.python.org/mkc'

bugs.python.org fields:

activity = <Date 2009-03-31.21:56:53.595>
actor = 'josiahcarlson'
assignee = 'none'
closed = True
closed_date = <Date 2009-03-31.16:41:43.917>
closer = 'josiahcarlson'
components = ['Library (Lib)']
creation = <Date 2008-02-11.22:17:31.712>
creator = 'mkc'
dependencies = []
files = []
hgrepos = []
issue_num = 2073
keywords = []
message_count = 12.0
messages = ['62294', '62360', '62364', '62365', '62366', '63983', '63995', '64055', '84832', '84886', '84888', '84929']
nosy_count = 6.0
nosy_names = ['akuchling', 'mkc', 'josiahcarlson', 'klimkin', 'giampaolo.rodola', 'intgr']
pr_nums = []
priority = 'normal'
resolution = 'out of date'
stage = None
status = 'closed'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue2073'
versions = ['Python 2.5']

@mkc
Copy link
Mannequin Author

mkc mannequin commented Feb 11, 2008

In asynchat, 'push' doesn't specify 'buffer_size', so the default of 512
is used. This is bogus and causes poor performance--it should use the
value of 'ac_out_buffer_size' instead.

@mkc mkc mannequin added stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Feb 11, 2008
@giampaolo
Copy link
Contributor

ac_out_buffer_size value is already used when sending data.
Look at initiate_send method:

    def initiate_send (self):
        obs = self.ac_out_buffer_size
        ...
        if self.ac_out_buffer and self.connected:
            try:
                num_sent = self.send (self.ac_out_buffer[:obs])
        ...

@mkc
Copy link
Mannequin Author

mkc mannequin commented Feb 13, 2008

The value is used there, but this is not effective in causing larger
packets to be sent, which I noticed by watching with strace. I think
the reason for this is that 'refill_buffer' will only make at most one
call to simple_producer.more, and that call will produce at most 512
bytes, because that's the default value of that argument, and its not
overridden in the call that creates the simple_producer inside of 'push'.

In addition, I see at least one other place in the code where the
constant '512' appears. Probably all of these should be changed to use
the 'ac_*' values, or at least a larger constant.

Looking at the big picture, though, I don't understand why we're trying
to break this stuff up in the first place. That seems like the job of
the OS, and it may well do it faster and better anyway. I would think
that every call to socket 'send' should try to ram as much data through
as possible. The return value will let us know what actually happened.

(In my application, send's of size >200K are regularly succeeding, as
seen with strace. I got this behavior by overriding 'push' with a fixed
version.)

@giampaolo
Copy link
Contributor

The value is used there, but this is not effective in causing larger
packets to be sent, which I noticed by watching with strace. I think
the reason for this is that 'refill_buffer' will only make at most one
call to simple_producer.more, and that call will produce at most 512
bytes, because that's the default value of that argument, and its not
overridden in the call that creates the simple_producer inside of
'push'.

Sorry, you're right.
I remember now that I reported a similar thing in bug bpo-1736190:
http://bugs.python.org/msg57581

In addition, I see at least one other place in the code where the
constant '512' appears.

Where? Aside from simple_producer.__init__ I don't see other places
where it is used.

@mkc
Copy link
Mannequin Author

mkc mannequin commented Feb 13, 2008

The other place I see the constant is in asyncore.py:

class dispatcher_with_send(dispatcher):

    def __init__(self, sock=None, map=None):
        dispatcher.__init__(self, sock, map)
        self.out_buffer = ''

    def initiate_send(self):
        num_sent = 0
        num_sent = dispatcher.send(self, self.out_buffer[:512])
        self.out_buffer = self.out_buffer[num_sent:]

This code seems to be undocumented and perhaps unused, but until/unless
it's deleted, it probably ought to be fixed.

@jafo jafo mannequin assigned gvanrossum Mar 18, 2008
@gvanrossum
Copy link
Member

I know nothing about asyncore. Does this problem still exist in the
trunk (2.6)?

@mkc
Copy link
Mannequin Author

mkc mannequin commented Mar 18, 2008

[Tracker bounced this the first time...]

I haven't run it, but just browsing the trunk source, it appears to
still be present. In fact, asynchat.py and asyncore.py have
apparently not been changed in two years. Andrew Kuchling would seem
to be the closest to this code (?), since medusa is apparently dead.

(More broadly, if these modules are going to stay, they really need to
be preened and better documented. It's not at all obvious, for
example, how the handle_error, handle_expt, and handle_close functions
all fit together. Is handle_error supposed to call handle_close, or
will the modules make that happen internally? etc.)

@giampaolo
Copy link
Contributor

As discussed on python-dev patch bpo-1736190 should be committed before
doing anything against asyncore or asynchat.

@gvanrossum gvanrossum removed their assignment Jan 6, 2009
@josiahcarlson
Copy link
Mannequin

josiahcarlson mannequin commented Mar 31, 2009

When push is called in the current trunk (as of 2.6), the data is
automatically chopped up into self.ac_out_buffer_size blocks for later
writing.

In order to force the use of the asynchat.simple_producer class (which
takes an argument for the block size to send), you must pass the
producer itself with push_with_producer() .

Closing as out of date.

@josiahcarlson josiahcarlson mannequin closed this as completed Mar 31, 2009
@mkc
Copy link
Mannequin Author

mkc mannequin commented Mar 31, 2009

Just to confirm, the real problem here was that tiny packets were being
sent out by default, and the obvious fix (altering ac_out_buffer_size)
didn't work.

Looking at the code, it appears that the change by Josiah Carlson
(bpo-64062) causes ac_out_buffer_size (which currently defaults to 4096) to
be obeyed.

Should that 4096 be larger? MTU for jumbo packets is ~9000, I think,
plus there's less system call overhead for larger values. (I use 62000
for my app, but I haven't studied alternatives lately.)

Also, there are still two constants '512' in asyncore/asynchat.
Shouldn't these be changed to match the 4096 default?

@giampaolo
Copy link
Contributor

In my experience I noticed that 65536 is a pretty good compromise, at
least when moving big amounts of data:
http://code.google.com/p/pyftpdlib/issues/detail?id=94

@josiahcarlson
Copy link
Mannequin

josiahcarlson mannequin commented Mar 31, 2009

The spare 512 values are for code that I expect no one is actually
using.

In terms of increasing the buffer size from 4096 to something larger,
that can be done, but I think that more than just a 10mbit switched lan
test should be performed. I would tend to believe that a larger size
would tend to perform better, but my concern is sending blocks too large
to the OS and having it say "sorry, but you need to chop it up more" via
sock.send() returning 1 or 2 (I've experienced this on Windows with
stdio and sockets).

Considering how easy it is to adjust, and considering that the value is
respected everywhere that matters (performance-conscious users aren't
using simple_producer, nor are they using dispatcher_with_send), I think
that 4096 is sufficient for the time being. Adjusting it up to 16k in
Python 2.8/3.2 seems reasonable to me. Someone ping me in a year ;) .

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants