Skip to content

Performance of consume_socket_output is n² where it could be just n #3349

@GeorchW

Description

@GeorchW

I noticed that the function is repeatedly concatenating bytes:

def consume_socket_output(frames, demux=False):
    ...
    # If the streams are demultiplexed, the generator yields tuples
    # (stdout, stderr)
    out = [None, None]
    for frame in frames:
        # It is guaranteed that for each frame, one and only one stream
        # is not None.
        assert frame != (None, None)
        if frame[0] is not None:
            if out[0] is None:
                out[0] = frame[0]
            else:
                out[0] += frame[0]
        else:
            if out[1] is None:
                out[1] = frame[1]
            else:
                out[1] += frame[1]
    return tuple(out)

This makes it extremely slow for long inputs. I noticed since I used dockerpy to get about 1GB of process output, which took hours and blocked 100% CPU for the entire time.

The fix for it should be pretty simple, just collect all the frames and use b''.join(frames) instead:

# If the streams are demultiplexed, the generator yields tuples
# (stdout, stderr)
stdout = []
stderr = []
for stdout_frame, stderr_frame in frames:
    # It is guaranteed that for each frame, one and only one stream
    # is not None.
    if stdout_frame:
        stdout.append(stdout_frame)
    else:
        stderr.append(stderr_frame)
stdout = b''.join(stdout) if len(stdout) > 0 else None
stderr = b''.join(stderr) if len(stderr) > 0 else None
return stdout, stderr

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions