Skip to content

Do we still need asciilib for better performance? #98229

Description

@sobolevn

Feature or enhancement

asciilib was introduced in c3cec78 as a performance feature for ascii strings.

11 years have passed since then.

While working on #98025 we with @vstinner experimented on unicode_count with and without asciilib_count calls.

Results were clear: it does not bring any benefits on our benchmarks.
And this commit was made: df3a6d9

Later, while working on #98228 I've noticed that asciilib_rsplit_whitespace also does not provide significant performance gains on my platform and my simple data input.

So, maybe this should be analyzed deeply?

Pitch

  • I think that these calls should be further investigated: do we really need this?
  • We should come up with better data to make a final decision, including:
    • What pytohn methods / c-api functions are affected?
    • Short and long ascii / non-ascii strings (because this check slows down all non-ascii strings by calling extra PyUnicode_IS_ASCII(str)
    • Windows / MacOS / Linux platforms, maybe the results will be different

I don't have access to Windows, but I can do the research for other platforms.

If it does not provide any performance benefits, it should be removed.

Activity

  1. mdboom commented on Oct 13, 2022

    @mdboom
    Contributor

    I'd generally be in favor of removing almost-duplicate code if there is no performance benefit.

    Have you done any analysis with the pyperformance benchmarks?

  2. vstinner commented on Oct 13, 2022

    @vstinner
    Member

    ASCII-only strings have multiple efficient properties. Examples:

    • Looking for a non-ASCII character in an ASCII string has complexity of O(1): it's always false
    • _PyUnicode_FromASCII() doesn't have to scan the string to compute the maximum code path: it's known that the string is ASCII
    • Maximum code point is 127 (STRINGLIB_MAX_CHAR=127 for asciilib functions)

    Some optimizations are implemented outside stringlib. Like:

    static Py_ssize_t
    any_find_slice(PyObject* s1, PyObject* s2, ...)
    {
        ...
        kind1 = PyUnicode_KIND(s1);
        kind2 = PyUnicode_KIND(s2);
        if (kind1 < kind2)
            return -1;
        ...
    }
    
  3. sobolevn commented on Oct 13, 2022

    @sobolevn
    MemberAuthor

    Have you done any analysis with the pyperformance benchmarks?

    Only very limited ones in #98025 (comment) and #98228 (comment)

  4. serhiy-storchaka commented on Oct 14, 2022

    @serhiy-storchaka
    Member

    Please perform tests also with longer strings (100, 1000, 10000 characters or more). Also test with strings containing non-ASCII whitespace characters \x85 and \xa0.

  5. rhpvorderman commented on Jan 24, 2023

    @rhpvorderman
    Contributor

    If you want to work with some real-world ASCII data you can download a FASTQ file named SRR15096500_1.fastq.gz here https://www.ebi.ac.uk/ena/browser/view/SRR15096500.
    FASTQ is a format to store DNA (or RNA) sequencing reads. You can find a description of the format here: https://en.wikipedia.org/wiki/FASTQ_format.

    The data can be quite big. Whole genome sequencing can yield FASTQ files that are 100GB when compressed with gzip.

    @marcelm has created a nice library called dnaio for parsing the format. To add to @vstinner ASCII has some really interesting other properties to (to complement @vstinner):

    • The last 5 bits of lowercase and uppercase characters are the same. Allowing quick case-independent comparing.
    • The last 4 bits of 0, 1, 2, 3, are 0000, 0001, 0010, 0011.. you get the idea
    • The fact that the highest code point is at most 127 allows you to create reasonably small lookup tables with only 128 entries.
    • The most significant bit of ASCII is always 0. So you can do a very quick check if strings are ascii by doing a Bitwise OR on all characters and checking if the result's significant bit is also zero. That trick is used in dnaio and runs at approximately 50GB/s (using SSE2 instructions) or 20GB/s (without). This could potentially be used in UTF-8 parsing.

    Anyway. I am curious to see if asciilib works well when using real-world data such as posted above. A lot of formats in my field are ASCII, so Python's performance in this area matters to me.

  6. vstinner commented on Apr 5, 2023

    @vstinner
    Member

    This could potentially be used in UTF-8 parsing.

    The utf8_decode() function of the Python "stringlib" internal library uses this property to check if a string is ASCII-only in an efficient way. It even works on size_t words (ex: 8 bytes on 64-bit systems) per loop iteration. See: Objects/stringlib/codecs.h.

  7. rhpvorderman commented on Apr 7, 2023

    @rhpvorderman
    Contributor

    The utf8_decode() function of the Python "stringlib" internal library uses this property to check if a string is ASCII-only in an efficient way.

    @vstinner I am aware that the current ASCII check exists and is quite efficient. However, it can be more efficient: #101289

    The current ASCII check is per string and involves a lot of code. A quick check on the input buffer is almost cost-free. The copying can then proceed using only PyUnicode_New(127) and memcpy. That is much faster. Although it will require some reworking of the ASCII/UTF-8 code.

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

    interpreter-core(Objects, Python, Grammar, and Parser dirs)performancePerformance or resource usagetype-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions