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

[SR-14102] Idiomatic way of checking if an Array<UInt8> starts with a bunch of bytes is slow #56488

Open
weissi opened this issue Jan 25, 2021 · 3 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. performance

Comments

@weissi
Copy link
Member

weissi commented Jan 25, 2021

Previous ID SR-14102
Radar rdar://problem/73570855
Original Reporter @weissi
Type Bug

Attachment: Download

Environment
docker.io/swiftlang/swift:nightly-main-bionic
Swift version 5.4-dev (LLVM 7459512566cf7f4, Swift 8b5869ce6217b1f)
Target: x86_64-unknown-linux-gnu
Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: cbaa9373048befa9b532f9d0f5ae9bbb

Issue Description:

Consider this code:

@inline(never)
func doIt(_ data: [UInt8]) -> Int {
    if data.starts(with: [1, 2, 3]) {
        return 1
    } else if data.starts(with: [4, 5, 6]) {
        return 2
    } else if data.starts(with: [0xff, 0xef, 0xdd]) {
        return 3
    } else {
        return 4
    }
}

You'd assume that Swift can do better than building three arrays and then doing starts(with🙂 on them. However, that's not the case...

swiftc -emit-assembly -O test.swift  | swift demangle | grep -A200 ^test.doIt | grep -e call -e 'retq'
    callq   __swift_instantiateConcreteTypeFromMangledName
    callq   swift_initStaticObject@PLT
    callq   swift_retain@PLT
    callq   swift_release@PLT
    callq   swift_initStaticObject@PLT
    callq   swift_retain@PLT
    callq   swift_release@PLT
    callq   swift_initStaticObject@PLT
    callq   swift_retain@PLT
    callq   swift_release@PLT
    retq

What's incredibly baffling is that if you rewrite it using Strings, then it generates much better code:

@inline(never)
func stringIt(_ data: [UInt8]) -> Int {
    if data.starts(with: "\u{0001}\u{00002}\u{00003}".utf8) {
        return 1
    } else if data.starts(with: "\u{0004}\u{0005}\u{0006}".utf8) {
        return 2
    // can't do this: } else if data.starts(with: [0xff, 0xef, 0xdd]) {
    // can't do this:    return 3
    } else {
        return 4
    }
}

Unfortunately that's super awkward and only works for unicode code units that are the same as their ASCII value, so I think that's 0 ... 127.

See here: the stringIt code has a retq before any callq

swiftc -emit-assembly -O test.swift  | swift demangle | grep -A200 ^test.stringIt | grep -e call -e 'retq'
    retq
        [...]
@weissi
Copy link
Member Author

weissi commented Jan 25, 2021

@swift-ci create

@weissi
Copy link
Member Author

weissi commented Jan 25, 2021

@milseman or maybe ravikandhadai (JIRA User)? good job in any case 🙂

@weissi
Copy link
Member Author

weissi commented Jan 25, 2021

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. performance
Projects
None yet
Development

No branches or pull requests

2 participants