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

Screen optimizations #160

Open
wants to merge 54 commits into
base: master
Choose a base branch
from

Commits on Jun 17, 2022

  1. Upgrade pyperf (drop support for Python 2.x)

    Since 0.8.1 pyte does not support Python 2.x anymore so it makes sense
    to upgrade one of its dev dependencies, pyperf.
    eldipa committed Jun 17, 2022
    Configuration menu
    Copy the full SHA
    8513fe8 View commit details
    Browse the repository at this point in the history
  2. Allow change the screen geometry

    Receive via environ the geometry of the screen to test with a
    default of 24 lines by 80 columns.
    Add this and the input file into Runner's metadata so it is preserved in
    the log file (if any)
    eldipa committed Jun 17, 2022
    Configuration menu
    Copy the full SHA
    cabc0a5 View commit details
    Browse the repository at this point in the history
  3. Impl benchmark tests for screen.display, .reset and .resize

    Implement three more benchmark scenarios for testing screen.display,
    screen.reset and screen.resize.
    
    For the standard 24x80 geometry, these methods have a negligible cost
    however of larger geometries, they can be up to 100 times slower than
    stream.feed so benchmarking them is important.
    
    Changed how the metadata is stored so on each bench_func call we encode
    which scenario are we testing, with which screen class and geometry.
    eldipa committed Jun 17, 2022
    Configuration menu
    Copy the full SHA
    940e19b View commit details
    Browse the repository at this point in the history

Commits on Jun 18, 2022

  1. Impl script to run a full benchmark

    A shell script to test all the captured input files and run them
    under different terminal geometries (24x80, 240x800, 2400x8000, 24x8000
    and 2400x80).
    
    These settings aim to stress pyte with larger and larger screens (by a
    10 factor on both dimensions and on each dimension separately).
    eldipa committed Jun 18, 2022
    Configuration menu
    Copy the full SHA
    0b8007a View commit details
    Browse the repository at this point in the history

Commits on Jul 2, 2022

  1. Fix benchmark.py using ByteStream and not Stream

    The input files in the tests/captured must be loaded with ByteStream and
    not Stream, otherwise the \r are lost and the benchmark results may not
    reflect real scenarios.
    eldipa committed Jul 2, 2022
    Configuration menu
    Copy the full SHA
    e0b0e8b View commit details
    Browse the repository at this point in the history

Commits on Jul 4, 2022

  1. Configuration menu
    Copy the full SHA
    eec4a2e View commit details
    Browse the repository at this point in the history
  2. display meth: iterate over data entries filling the gap between

    The former `for x in range(...)` implementation iterated over the all
    the possibly indexes (for columns and lines) wasting cyclies because
    some of those indexes (and in some cases most) pointed to non-existing
    entries.
    
    These non-existing entries were faked and a default character was
    returned in place.
    
    This commit instead makes display to iterate over the existing entries.
    When gaps between to entries are detected, the gap is filled with the
    same default character without having to pay for indexing non-entries.
    
    Note: I found that in the current implementation of screen,
    screen.buffer may have entries (chars in a line) outside of the width of
    the screen. At the display method those are filtered out however I'm not
    sure if this is not a real bug that was uncovered because never we
    iterated over the data entries. If this is true, we may be wasting space
    as we keep in memory chars that are outside of the screen.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    f899535 View commit details
    Browse the repository at this point in the history
  3. Inline generator into display inner loop

    Python generators (yield) and function calls are slower then normal
    for-loops. Improve screen.display by x1 to x1.8 times faster by
    inlining the code.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    b3b7db4 View commit details
    Browse the repository at this point in the history
  4. Move assert out of prod code

    The assert that checks the width of each char is removed from
    screen.display and put it into the tests. This ensures that our test
    suite maintains the same quality and at the same time we make
    screen.display ~x1.7 faster.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    de59245 View commit details
    Browse the repository at this point in the history
  5. Cache in Char its width

    Instead of computing it on each screen.display, compute the width of the
    char once on screen.draw and store it in the Char tuple.
    
    This makes screen.display ~x1.10 to ~x1.20 faster and it makes
    stream.feed only ~x1.01 slower in the worst case. This negative impact
    is due the change on screen.draw but measurements on my lab show
    inconsistent results (stream.feed didn't show a consistent performance
    regression and ~x1.01 slower was the worst value that I've got).
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    020fce6 View commit details
    Browse the repository at this point in the history
  6. Pre-fetch attributes on screen.draw (x1.20 to x2.0 faster)

    Fetch some attributes that were frequently accessed in the for-loop of
    screen.draw avoiding accessing them on each iteration.
    
    Most of them remain constant within the draw() method anyways. Others,
    like cursor.x, cursor.y and line are updated infrequently inside the
    for-loop so it still faster pre-fetch them outside and update them if
    needed than accessing them on each iteration.
    
    Benchmark results show stream.feed is x1.20 to x2.0 faster with these
    optimizations. Benchmark files that have more control sequences (like
    htop, mc and vim) have a lower improvement as the parsing of these
    sequences dominates the runtime of stream.feed.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    8e7ee07 View commit details
    Browse the repository at this point in the history
  7. Allow temporal cursor_x > columns (x1.05 to x1.14 faster)

    Instead of checking if cursor_x > columns at the end of iteration
    and set cursor_x to the minimum of (cursor_x and columns), delay
    that decision to the begin of the next iteration or at the end
    of the for-loop.
    
    This removes one "if" statement at the end of the for-loop and allows us
    to use the local variable cursor_x all the time without having to update
    cursor.x. Only this happens before insert_characters() and at the end of
    the draw() method when the cursor.x is been visible by code outside
    draw() and therefore must be updated with the latest value of cursor_x.
    
    This optimization makes stream.feed between x1.05 and x1.14 faster. As
    in any optimization on draw(), the use cases that gets more improvements
    are the ones that have very few control sequences in their input (so
    stream.feed is dominated by screen.draw and not be stream._parse_fsm)
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    d4d2e4a View commit details
    Browse the repository at this point in the history
  8. Make Char mutable (not a namedtuple) (API may break)

    Make Char mutable (and ordinary object) so we can modify each char in
    place avoiding calling _replace.
    
    This commit only changed the Char class and implements some methods to
    emulate the namedtuple API.
    
    Theoretically it could be possible to emulate the whole namedtuple API
    but it is unclear if it worth. In this scenario, user code may break.
    
    Using a plain object instead of a namedtuple added a regression on
    memory usage of x1.20 for htop and mc benchmark files when HistoryScreen was
    used. The rest of the benchmarks didn't change significantly (but it is
    expected to be slightly more inefficient).
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    945b19b View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    e881d25 View commit details
    Browse the repository at this point in the history
  10. Reuse/share char styles (x1.05 to x1.30 lighter)

    Reduce the memory footprint reusing/sharing the same CharStyle object
    among different Char instances.
    
    A specialized _replace_data changes the data and width of the char but
    not its style. This reduces the footprint between x1.05 and x1.30 with
    respect the 0.8.1-memory.json baseline results.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    5f784ec View commit details
    Browse the repository at this point in the history
  11. Update Chars in-place instead of recreating them (x1.20 and x1.90 fas…

    …ter; regress on mem)
    
    Instead of calling _replace() to create a new Char object, modify the
    existing one.
    
    For that, the Line (ex StaticDefaultDict) is in charge to
    fetch the char and do the modifications. If no Char is found, only then
    a Char is created and inserted in the Line (dict). See write_data().
    
    In some cases we need to get a Char, read it and then update it so a
    copy of the Line's default char is returned and added to the line. A
    copy is required because now the Char are mutable. See char_at().
    
    Changed the API of Char: _asdict renamed as as_dict and  _replace as
    copy_and_change; removed _replace_data and added a copy method.
    
    The constructor also changed: it is required data, width and style. The
    former way to construct a Char can be done with from_attributes class
    method.
    
    This commits improved the runtime of stream.feed by x1.20 to x1.90
    (faster) however a regression on the memory footprint was found (between
    x1.10 and x1.50). I don't have an explanation for this last point.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    9721698 View commit details
    Browse the repository at this point in the history
  12. Fix test_reverse_index (history) due old API

    The test was using a legacy API of screen.buffer when the buffer was a
    dense matrix. Now it is sparse we cannot use len(screen.buffer) anymore
    or buffer[-1] either.
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    e49fb3f View commit details
    Browse the repository at this point in the history
  13. Use binary search over non-empty lines on index/reverse_index

    This improvement impacts slighly negatively over small geometries (x1.01
    to x1.05 slower) but improves on larger geometries and for almost all
    the cases of HistoryScreen (x1.10 to x1.20)
    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    d94299d View commit details
    Browse the repository at this point in the history
  14. Minor optimizations.

    eldipa committed Jul 4, 2022
    Configuration menu
    Copy the full SHA
    912028f View commit details
    Browse the repository at this point in the history

Commits on Jul 5, 2022

  1. Calculate statistics about buffer's and lines' internals (no stable API)

    It is handy to get some stats about the layout and chars locations in
    the lines/buffer and see how sparse they are.
    
    The statistics are not part of the stable API so they may change between
    versions.
    eldipa committed Jul 5, 2022
    Configuration menu
    Copy the full SHA
    4c04935 View commit details
    Browse the repository at this point in the history
  2. On screen.buffer return a read-only view (BufferView/LineView)

    This layer of abstraction will allow use to changes on the real buffer
    without breaking the public API.
    eldipa committed Jul 5, 2022
    Configuration menu
    Copy the full SHA
    84cd21f View commit details
    Browse the repository at this point in the history
  3. Minor lookup prefetch.

    eldipa committed Jul 5, 2022
    Configuration menu
    Copy the full SHA
    5ae46bc View commit details
    Browse the repository at this point in the history
  4. Do not unintentionally create empty lines

    Because screen._buffer is a defaultdict, an access to a non-existent
    element has the side effect
    eldipa committed Jul 5, 2022
    Configuration menu
    Copy the full SHA
    da66a7e View commit details
    Browse the repository at this point in the history

Commits on Jul 6, 2022

  1. Add blankcs Stats

    eldipa committed Jul 6, 2022
    Configuration menu
    Copy the full SHA
    1fd373a View commit details
    Browse the repository at this point in the history
  2. Use a space for padding screen.display

    screen.default_char is always the space character so instead of using
    screen.default_char for padding we use the space character directly.
    eldipa committed Jul 6, 2022
    Configuration menu
    Copy the full SHA
    b8250ea View commit details
    Browse the repository at this point in the history
  3. Replace line's default style instead overwriting its char

    Because the default char of a line is always the space character, we
    don't need to overwrite it with screen.default_char, just we
    need to change its style.
    eldipa committed Jul 6, 2022
    Configuration menu
    Copy the full SHA
    8d71528 View commit details
    Browse the repository at this point in the history
  4. Fix a bug on index that top line was not removed.

    If the next line of the margin's top was not empty, the for-loop used
    that entry to override the top line working as expected.
    
    But when the next line of the top was empty, the top line was untouched
    so an explicit pop is required.
    
    A similar issue happen on reverse_index.
    
    Both bugs were covered due a side effect to iterating over
    screen.buffer: on each line lookup, if no such exist, a new line is
    added to the buffer. This added new line then was used to override the
    top on an index() call.
    eldipa committed Jul 6, 2022
    Configuration menu
    Copy the full SHA
    71ab12a View commit details
    Browse the repository at this point in the history
  5. Configuration menu
    Copy the full SHA
    01f96bc View commit details
    Browse the repository at this point in the history

Commits on Jul 7, 2022

  1. Try to delete entries on erase instead of write spaces

    If the line's default char and the cursor's attributes are the same,
    instead of writing spaces into the line, delete the chars.
    
    This should be equivalent from user's perspective.
    
    This applies to erase_characters, erase_in_line and erase_in_display.
    
    This optimization reduced the number of false lines (lines without any
    char) and the number of blank lines (lines with only spaces).
    With less entries in the buffer, the rest of the iterations can
    take advantage of the sparsity of the buffer and process much less.
    eldipa committed Jul 7, 2022
    Configuration menu
    Copy the full SHA
    70d4b15 View commit details
    Browse the repository at this point in the history

Commits on Jul 8, 2022

  1. Impl prev_page/next_page with sparse iteration

    We avoid a full scan and instead we do a sparse iteration. This also
    avoids adding empty lines into the buffer as real entries when a
    non-existing entry has the same effect and it consumes less memory.
    eldipa committed Jul 8, 2022
    Configuration menu
    Copy the full SHA
    4a15d3a View commit details
    Browse the repository at this point in the history
  2. Make lines in history a LineView and make Line raise on non-key

    Instead of storing Line objects in the history's top and bottom lists,
    store LineView of them. This makes them read-only and allows us to make
    Line a full dict without an overloaded __missing__.
    
    Skipping __missing__ ensures that the code of Screen does not access an
    x position that does not exist by mistake.
    eldipa committed Jul 8, 2022
    Configuration menu
    Copy the full SHA
    6b4f088 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    13cf059 View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    a4e7ed5 View commit details
    Browse the repository at this point in the history
  5. Configuration menu
    Copy the full SHA
    3056742 View commit details
    Browse the repository at this point in the history
  6. Impl repr of a Char

    eldipa committed Jul 8, 2022
    Configuration menu
    Copy the full SHA
    f96ab6b View commit details
    Browse the repository at this point in the history
  7. Configuration menu
    Copy the full SHA
    70763b6 View commit details
    Browse the repository at this point in the history
  8. Configuration menu
    Copy the full SHA
    51e79a6 View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    47d7c62 View commit details
    Browse the repository at this point in the history
  10. Configuration menu
    Copy the full SHA
    b4258e1 View commit details
    Browse the repository at this point in the history

Commits on Jul 9, 2022

  1. Configuration menu
    Copy the full SHA
    f178712 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    05c8c2c View commit details
    Browse the repository at this point in the history
  3. Impl resize with sparse iter

    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    80aa50a View commit details
    Browse the repository at this point in the history
  4. Optional use a dummy set for tracking dirty lines

    By default Screen tracks which lines are dirty and should be of interest
    for the user. This functionality may not be of interest for all the use
    cases and this tracking is expensibe, specially for large geometries.
    
    If track_dirty_lines is set to False, the screen.dirty attribute becomes
    a dummy or null set that it is always empty saving memory and time.
    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    1bee165 View commit details
    Browse the repository at this point in the history
  5. Binary search for the tabstop

    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    065b31d View commit details
    Browse the repository at this point in the history
  6. Make (0, lines-1) the default margin instead of None

    Instead of using None as a special case, set the margins to (0, lines-1)
    by default.
    
    This may break user code if the user is expecting None as a valid value
    or if it is setting it. If required we could make screen.margins a
    property and hide the internal implementation.
    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    8f11049 View commit details
    Browse the repository at this point in the history
  7. Make internal _buffer a dict and not a defaultdict

    Using a defaultdict can easily introduce false entries in the buffer
    making it less sparse. The new Buffer class supports all the dict's operations
    but it does not add an entry if the key is missing.
    
    To add a new entry (a new line), do a buffer.line_at(y). This is
    equivalent to dict.setdefault(y, new_line()) but avoids the call to
    new_line() if an entry y exists.
    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    01b7d56 View commit details
    Browse the repository at this point in the history
  8. Optionally disable display graphic attributes

    When the graphic attributes are disabled, select_graphic_rendition
    always set the default style. With this, the lines' default char and the
    cursor's char will always match and the screen will optimize the erase*
    methods.
    eldipa committed Jul 9, 2022
    Configuration menu
    Copy the full SHA
    e3fdf41 View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    7839ded View commit details
    Browse the repository at this point in the history

Commits on Jul 10, 2022

  1. Configuration menu
    Copy the full SHA
    2ca29a5 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    c589265 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    1b42c89 View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    c65ac82 View commit details
    Browse the repository at this point in the history
  5. Impl compressed_display to optionally avoid left/right spaces, top/bo…

    …ttom empty lines
    
    This is an optimization over screen.display where it is possible to
    strip left/right spaces of the lines and/or filter top/bottom whole
    empty lines.
    
    It is implemented in an opportunistic fashion so it may not fully
    strip/filter all that it is expected.
    
    In particular, lines with left or right spaces with non-default
    attributes are not stripped; lines that contains only spaces at the top
    or bottom are not filtered (even if lstrip/rstrip is set).
    
    This implementation is meant to be used when the screen has very large
    geometries and screen.display wastes too much time and memory on padding
    large lines and/or filling with a lot of empty lines.
    eldipa committed Jul 10, 2022
    Configuration menu
    Copy the full SHA
    f4ea46a View commit details
    Browse the repository at this point in the history

Commits on Jul 12, 2022

  1. Configuration menu
    Copy the full SHA
    ba980a0 View commit details
    Browse the repository at this point in the history