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-9440] String should provide a string-switch (multi-compare) #51904

Open
milseman mannequin opened this issue Dec 7, 2018 · 10 comments
Open

[SR-9440] String should provide a string-switch (multi-compare) #51904

milseman mannequin opened this issue Dec 7, 2018 · 10 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself performance standard library Area: Standard library umbrella

Comments

@milseman
Copy link
Mannequin

milseman mannequin commented Dec 7, 2018

Previous ID SR-9440
Radar rdar://problem/48680971
Original Reporter @milseman
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler, Standard Library
Labels Bug, Performance
Assignee None
Priority Medium

md5: 811f73a6f0ff81d39486a5037b657dff

Issue Description:

String should provide an API usable from the compiler and optimizer that takes one string and compares it for equality against a series of other strings, saving code size and improving performance.

This would be a more efficient way of lowering a string switch. Currently, a string switch is lowered into a series of `if str == ...`. Benefits:

1. String equality has a fast-path checking bitwise comparison of the struct itself, but for a `N`-length switch the likelihood of that being hit is `1/N` at best. Inlining that otherwise-good check causes code size bloat.

2. String equality has to introspect each side of the comparison. A series of equality checks re-introspects the same string every time. This has runtime costs.

We might also be able to unify this with the existing code that does it for some String enums.

@milseman
Copy link
Mannequin Author

milseman mannequin commented Dec 7, 2018

For example, see https://bugs.swift.org/browse/SR-9426

@zoecarver
Copy link
Collaborator

Mind if I take this one? I will probably need a bit of guidance. I looked at the ICMP_EQ builtin, that got me started, but I am not exactly sure what method to use when comparing the strings themselves. Maybe just calling strcmp will be good enough?

@milseman
Copy link
Mannequin Author

milseman mannequin commented Aug 13, 2019

String honors Unicode Canonical Equivalence, so you will need to do the same. If you look at StringComparison.swift, you'll see the underlying methods to call for comparison. The series of `if` checks redundantly introspects the string being switched over, while a multi-compare function could introspect the string once.

@eeckstein might be able to help you with the compiler-library interface. We want the function to take many strings to compare against, but don't want to actually allocate an array for the arguments.

@zoecarver
Copy link
Collaborator

I did briefly look at the implementation in the standard library. Thanks for the overview of the requirements. I will reach out to Erik and work on this a bit more tomorrow. I will post my questions here unless there is a better place.

Thanks for the quick response and help.

@eeckstein
Copy link
Member

We already have this kind of thing for the init(rawValue: String) of string enums.
You might want to look at #11936
The library functions in https://github.com/apple/swift/blob/master/stdlib/public/core/StringSwitch.swift could also be used for general String switches or cascades of if-String-comparisons.
A String switch could be replaced by a call to _findStringSwitchCase/_findStringSwitchCaseWithCache and then switched over the returned integer (which can then be optimized by LLVM into a jump table).

@zoecarver
Copy link
Collaborator

Thanks @eeckstein! I will play around with that a bit today and see if I can get something working.

@zoecarver
Copy link
Collaborator

To clarify the goal, we want something like the following to produce a single block of assembly with no jumps (like what would happen if the arguments of type String were replaced with type Int):

func test(a: String, b: String, c: String, d: String) -> Bool {
    if a == b {
        if b == c {
            return c == d
        }
        return false
    }
    return false
}

In this case, the compiler would have to realize that there was a nested set of if-statments and optimize those out. What would be the best method to do this? I assume it should be done in the SILOptimizer. In the PR you linked, you replace one string comparison function with another, would a function like that suffice, or is more complicated logic required?

@eeckstein
Copy link
Member

I don't think this is an important code pattern to optimize. It's doable in the optimizer, but it's very specific and there must be a good reason to optimize this kind of pattern.
IMO, the important scenario is if a (non constant) string value is compared against multiple string constants, like in https://github.com/apple/swift/blob/master/benchmark/single-source/Calculator.swift

@zoecarver
Copy link
Collaborator

I see. So the optimization is inlining all the comparisons, so fewer branches are created?

@zoecarver
Copy link
Collaborator

This is a slightly different issue but, I find it interesting that the assembly produced by the following program does not change significantly when `input` is switched on instead of `x`:

let x = "0"

func test(input: String) -> Int {
  switch x {
    case "0": return 0
    case "1": return 1
    case "2": return 2
    case "3": return 3
    case "4": return 4
    case "5": return 5
    case "6": return 6
    case "7": return 7
    case "8": return 8
    case "9": return 9
    default: return 0
  }
}

@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. compiler The Swift compiler in itself performance standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

2 participants