Skip to content

GlobalOpt: optimize static properties with resilient types. #21614

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

Merged
merged 3 commits into from
Jan 4, 2019

Conversation

eeckstein
Copy link
Contributor

For example:

struct ResilientStruct {
  var singleField: Int
  public static let x = ResilientStruct(singleField: 27)
}

The generated (resilient) getter for x should just return a 27 (wrapped in a struct) and not lazily initialize a global variable.

The main SIL infrastructure change for this is to store type lowerings for multiple resilience expansions.
This lets the SIL optimizer reason about types (e.g. if a type is loadable) in specific functions.
For example, a type might be loadable in a resilient function, but not in an inlinable function.

rdar://problem/46712028

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How important is this optimization? Can it wait until after opaque values?

}

// Search for a matching lowering in the linked list of lowerings.
while (true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks O(n), can we do this better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but currently it's not worth the effort, as n = 1.
In case we add many more resilience expansions in the future we can think of a more complicated lookup mechanism.

bool isLoadableOrOpaque(SILType Ty) {
if (!F) {
// We are inserting into the static initializer of a SILGlobalVariable.
// All types used there are loadable by definition.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the emitter for the initializer has a bug? I think you still want to use ResilienceExpansion::Maximal here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this does not work. The conservative assumption would trigger a false assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, you are saying ResilienceExpansion::Maximal. Yeah, that's a good idea.

@@ -1139,7 +1139,7 @@ namespace {
/// Build the appropriate TypeLowering subclass for the given type,
/// which is assumed to already have been lowered.
class LowerType
: public TypeClassifierBase<LowerType, const TypeLowering *>
: public TypeClassifierBase<LowerType, TypeLowering *>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last time I tried to plumb through something like this I had to change a lot more code. In particular it looks like class LowerType has a number of recursive calls to TC.getTypeLowering(), and we're not passing the expansion down through them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at #5524, where I was trying to do the same thing but with a generic signature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, as I see it, the Expansion is passed down recursively (with classifyType).
Except in visitTupleType - maybe this is a bug?
Anyway, in case it's not passed down in some cases, it just makes the analysis more conservative.

@swift-ci
Copy link
Contributor

swift-ci commented Jan 3, 2019

Build comment file:

Performance: -O

TEST OLD NEW DELTA RATIO
Regression
InsertCharacterEndIndexNonASCII 55 61 +10.9% 0.90x (?)
Improvement
StringToDataEmpty 700 650 -7.1% 1.08x (?)

Performance: -Osize

TEST OLD NEW DELTA RATIO
Improvement
Breadcrumbs.CopyUTF16CodeUnits.Mixed 68 57 -16.2% 1.19x
StringBuilderSmallReservingCapacity 379 338 -10.8% 1.12x
StringBuilder 364 328 -9.9% 1.11x

Performance: -Onone

TEST OLD NEW DELTA RATIO
Improvement
ArrayOfPOD 858 774 -9.8% 1.11x (?)

Code size: -swiftlibs

TEST OLD NEW DELTA RATIO
Improvement
libswiftAppKit.dylib 77824 69632 -10.5% 1.12x
libswiftDispatch.dylib 98304 94208 -4.2% 1.04x
How to read the data The tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.

If you see any unexpected regressions, you should consider fixing the
regressions before you merge the PR.

Noise: Sometimes the performance results (not code size!) contain false
alarms. Unexpected regressions which are marked with '(?)' are probably noise.
If you see regressions which you cannot explain you can try to run the
benchmarks again. If regressions still show up, please consult with the
performance team (@eeckstein).

Hardware Overview
  Model Name: Mac Pro
  Model Identifier: MacPro6,1
  Processor Name: 12-Core Intel Xeon E5
  Processor Speed: 2.7 GHz
  Number of Processors: 1
  Total Number of Cores: 12
  L2 Cache (per Core): 256 KB
  L3 Cache: 30 MB
  Memory: 64 GB
--------------

@slavapestov
Copy link
Contributor

Now that I think about it wouldn’t it make more sense to change SILGen to use the new SILFunction-specific entry point? Then you won’t need your new peephole at all. This was going to be my approach when I wanted to implement resilience-aware type lowering in SIL.

@eeckstein
Copy link
Contributor Author

I experimented with that a bit, but it didn't work out. The reason is that the initializer of the resilient struct still has to return the struct indirectly. Only after inlining this initializer into the globalinit function the peephole can do its work.

@slavapestov
Copy link
Contributor

@eeckstein ah, I see. In any case we should eventually fix SILGen to do the right thing here. Also once we get opaque values, SILGen won't be initializing structs in memory in-place with struct_element_addr anymore; the whole Initialization thing will go away.

So I'm fine with landing this as-is, if you fix the >80 char lines and also fix the TupleType case.

public struct ResilientStruct {
var rawValue: Int

public static let staticVal = ResilientStruct(rawValue: 27)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some more test case too though? Global variables, as well as maybe statics with tuple type, or something involving enums.

This lets the SIL optimizer reason about types (e.g. if a type is loadable) in specific functions.
For example, a type might be loadable in a resilient function, but not in an inlinable function.
…ilience.

Allow creating instructions with types which are only loadable in resilient functions (but not in all functions).
For example:
struct ResilientStruct {
  var singleField: Int
  public static let x = ResilientStruct(singleField: 27)
}

The generated (resilient) getter for x should just return a 27 (wrapped in a struct) and not lazily initialize a global variable.

rdar://problem/46712028
@eeckstein eeckstein force-pushed the resilient-globalopt branch from f8f39ca to aa5a4e1 Compare January 4, 2019 19:21
@eeckstein
Copy link
Contributor Author

I will fix the tuple case and add more tests in a follow-up PR. This GlobalOpt peephole is pretty limited and I cannot create test cases for more complex types with it. I'll use the new isLoadable API in other optimizations where I can create more test cases for it.

@eeckstein
Copy link
Contributor Author

@swift-ci smoke test

@eeckstein eeckstein merged commit 3275f56 into swiftlang:master Jan 4, 2019
@eeckstein eeckstein deleted the resilient-globalopt branch January 4, 2019 21:21
@slavapestov
Copy link
Contributor

@eeckstein Ok, thanks. I'm going to revisit this code soon since I want to implement this optimization at the SILGen level as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants