Skip to content

Cache struct/class field offsets in SIL. #24717

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
May 14, 2019
Merged

Cache struct/class field offsets in SIL. #24717

merged 3 commits into from
May 14, 2019

Conversation

atrick
Copy link
Contributor

@atrick atrick commented May 12, 2019

The field's ordinal value is used by the Projection abstraction, which is
the basis of efficiently comparing and sorting access paths in SIL. It must
be cached before it is used by any SIL passes, including the verifier, or it
causes widespread quadratic complexity.

Fixes rdar://problem/50353228 Swift compile time regression with optimizations enabled

In production code, a file that was taking 40 minutes to compile now
takes 1 minute, with more than half of the time in LLVM.

Here's a short script that reproduces the problem. It used to take 30s and now takes 0.06s:

// ./genlazyinit.swift > lazyinit.sil
// sil-opt ./lazyinit.sil --access-enforcement-opts

var NumProperties = 300

print("""
      sil_stage canonical

      import Builtin
      import Swift
      import SwiftShims

      public class LazyProperties {
      """)

for i in 0..<NumProperties {
  print("""
          //  public lazy var i\(i): Int { get set }
          @_hasStorage @_hasInitialValue final var __lazy_storage__i\(i): Int? { get set }
        """)
}

print("""
      }

     // LazyProperties.init()
     sil @$s4lazy14LazyPropertiesCACycfc : $@convention(method) (@owned LazyProperties) -> @owned LazyProperties {
     bb0(%0 : $LazyProperties):
       %enum = enum $Optional<Int>, #Optional.none!enumelt
     """)

for i in 0..<NumProperties {
  let adr = (i*4) + 2
  let access = adr + 1
  print("""
          %\(adr) = ref_element_addr %0 : $LazyProperties, #LazyProperties.__lazy_storage__i\(i)
          %\(access) = begin_access [modify] [dynamic] %\(adr) : $*Optional<Int>
          store %enum to %\(access) : $*Optional<Int>
          end_access %\(access) : $*Optional<Int>
        """)
}

print("""
        return %0 : $LazyProperties
      } // end sil function '$s4lazy14LazyPropertiesCACycfc'
      """)

@atrick
Copy link
Contributor Author

atrick commented May 12, 2019

@swift-ci test.

@atrick
Copy link
Contributor Author

atrick commented May 12, 2019

@swift-ci test source compatibility.

@atrick
Copy link
Contributor Author

atrick commented May 12, 2019

@swift-ci test compiler performance

@atrick atrick requested a review from gottesmm May 12, 2019 02:47
@swift-ci
Copy link
Contributor

Summary for master full

Unexpected test results, excluded stats for ProcedureKit, Tagged, Wordy, Deferred

No regressions above thresholds

Debug-batch

debug-batch brief

Regressed (0)
name old new delta delta_pct
Improved (0)
name old new delta delta_pct
Unchanged (delta < 1.0% or delta < 100.0ms) (3)
name old new delta delta_pct
Frontend.NumInstructionsExecuted 31,027,272,793,047 31,004,520,598,226 -22,752,194,821 -0.07%
LLVM.NumLLVMBytesOutput 1,168,295,678 1,168,291,490 -4,188 -0.0%
time.swift-driver.wall 2861.5s 2863.1s 1.7s 0.06%

debug-batch detailed

Regressed (0)
name old new delta delta_pct
Improved (0)
name old new delta delta_pct
Unchanged (delta < 1.0% or delta < 100.0ms) (107)
name old new delta delta_pct
AST.NumASTBytesAllocated 72,403,623,041 72,477,644,767 74,021,726 0.1%
AST.NumDecls 94,750 94,750 0 0.0%
AST.NumDependencies 213,723 213,731 8 0.0%
AST.NumImportedExternalDefinitions 1,212,541 1,212,541 0 0.0%
AST.NumInfixOperators 35,705 35,705 0 0.0%
AST.NumLinkLibraries 0 0 0 0.0%
AST.NumLoadedModules 255,366 255,366 0 0.0%
AST.NumLocalTypeDecls 123 123 0 0.0%
AST.NumObjCMethods 15,429 15,429 0 0.0%
AST.NumPostfixOperators 18 18 0 0.0%
AST.NumPrecedenceGroups 17,488 17,488 0 0.0%
AST.NumPrefixOperators 90 90 0 0.0%
AST.NumReferencedDynamicNames 122 122 0 0.0%
AST.NumReferencedMemberNames 4,163,187 4,163,187 0 0.0%
AST.NumReferencedTopLevelNames 323,183 323,183 0 0.0%
AST.NumSourceBuffers 406,343 406,343 0 0.0%
AST.NumSourceLines 3,165,267 3,165,267 0 0.0%
AST.NumSourceLinesPerSecond 2,333,353 2,345,013 11,660 0.5%
AST.NumTotalClangImportedEntities 4,629,709 4,631,055 1,346 0.03%
Driver.ChildrenMaxRSS 112,210,778,112 112,309,792,768 99,014,656 0.09%
Driver.DriverDepCascadingDynamic 0 0 0 0.0%
Driver.DriverDepCascadingExternal 0 0 0 0.0%
Driver.DriverDepCascadingMember 0 0 0 0.0%
Driver.DriverDepCascadingNominal 0 0 0 0.0%
Driver.DriverDepCascadingTopLevel 0 0 0 0.0%
Driver.DriverDepDynamic 0 0 0 0.0%
Driver.DriverDepExternal 0 0 0 0.0%
Driver.DriverDepMember 0 0 0 0.0%
Driver.DriverDepNominal 0 0 0 0.0%
Driver.DriverDepTopLevel 0 0 0 0.0%
Driver.NumDriverJobsRun 19,330 19,330 0 0.0%
Driver.NumDriverJobsSkipped 0 0 0 0.0%
Driver.NumDriverPipePolls 203,439 203,061 -378 -0.19%
Driver.NumDriverPipeReads 226,645 227,185 540 0.24%
Driver.NumProcessFailures 0 0 0 0.0%
Frontend.MaxMallocUsage 742,078,261,208 742,215,042,704 136,781,496 0.02%
Frontend.NumInstructionsExecuted 31,027,272,793,047 31,004,520,598,226 -22,752,194,821 -0.07%
Frontend.NumProcessFailures 0 0 0 0.0%
IRModule.NumIRAliases 121,583 121,583 0 0.0%
IRModule.NumIRBasicBlocks 4,685,237 4,685,237 0 0.0%
IRModule.NumIRComdatSymbols 0 0 0 0.0%
IRModule.NumIRFunctions 2,132,840 2,132,840 0 0.0%
IRModule.NumIRGlobals 2,238,525 2,238,525 0 0.0%
IRModule.NumIRIFuncs 0 0 0 0.0%
IRModule.NumIRInsts 61,569,845 61,569,845 0 0.0%
IRModule.NumIRNamedMetaData 93,930 93,930 0 0.0%
IRModule.NumIRValueSymbols 3,901,710 3,901,710 0 0.0%
LLVM.NumLLVMBytesOutput 1,168,295,678 1,168,291,490 -4,188 -0.0%
Parse.NumFunctionsParsed 174,293 174,293 0 0.0%
Parse.NumIterableDeclContextParsed 1,188,598 1,188,598 0 0.0%
SILModule.NumSILGenDefaultWitnessTables 0 0 0 0.0%
SILModule.NumSILGenFunctions 1,101,363 1,101,363 0 0.0%
SILModule.NumSILGenGlobalVariables 41,604 41,604 0 0.0%
SILModule.NumSILGenVtables 12,415 12,415 0 0.0%
SILModule.NumSILGenWitnessTables 46,811 46,811 0 0.0%
SILModule.NumSILOptDefaultWitnessTables 0 0 0 0.0%
SILModule.NumSILOptFunctions 1,566,803 1,566,803 0 0.0%
SILModule.NumSILOptGlobalVariables 42,499 42,499 0 0.0%
SILModule.NumSILOptVtables 20,589 20,589 0 0.0%
SILModule.NumSILOptWitnessTables 102,467 102,467 0 0.0%
Sema.AccessLevelRequest 2,849,921 2,850,223 302 0.01%
Sema.AttachedPropertyDelegateRequest 1,456,288 1,456,288 0 0.0%
Sema.AttachedPropertyDelegateTypeRequest 279,543 279,543 0 0.0%
Sema.CustomAttrNominalRequest 0 0 0 0.0%
Sema.DefaultAndMaxAccessLevelRequest 63,690 63,686 -4 -0.01%
Sema.DefaultTypeRequest 383,514 383,514 0 0.0%
Sema.EnumRawTypeRequest 18,268 18,268 0 0.0%
Sema.ExtendedNominalRequest 4,101,892 4,103,081 1,189 0.03%
Sema.InheritedDeclsReferencedRequest 4,602,465 4,608,696 6,231 0.14%
Sema.InheritedTypeRequest 241,776 241,670 -106 -0.04%
Sema.IsDynamicRequest 2,220,939 2,220,939 0 0.0%
Sema.IsFinalRequest 4,016,929 4,019,372 2,443 0.06%
Sema.IsObjCRequest 1,948,094 1,948,139 45 0.0%
Sema.MangleLocalTypeDeclRequest 246 246 0 0.0%
Sema.NamedLazyMemberLoadFailureCount 23,486 23,523 37 0.16%
Sema.NamedLazyMemberLoadSuccessCount 20,572,505 20,572,479 -26 -0.0%
Sema.NominalTypeLookupDirectCount 34,281,450 34,289,461 8,011 0.02%
Sema.NumConformancesDeserialized 6,891,992 6,899,873 7,881 0.11%
Sema.NumConstraintScopes 19,771,142 19,771,398 256 0.0%
Sema.NumConstraintsConsideredForEdgeContraction 52,019,612 52,019,836 224 0.0%
Sema.NumDeclsDeserialized 51,944,265 51,976,833 32,568 0.06%
Sema.NumDeclsFinalized 2,012,899 2,012,899 0 0.0%
Sema.NumDeclsTypechecked 1,040,157 1,040,157 0 0.0%
Sema.NumDeclsValidated 2,428,489 2,428,498 9 0.0%
Sema.NumFunctionsTypechecked 1,076,370 1,076,370 0 0.0%
Sema.NumGenericSignatureBuilders 1,176,457 1,176,606 149 0.01%
Sema.NumLazyGenericEnvironments 10,454,584 10,458,050 3,466 0.03%
Sema.NumLazyGenericEnvironmentsLoaded 224,392 224,320 -72 -0.03%
Sema.NumLazyIterableDeclContexts 7,246,757 7,249,353 2,596 0.04%
Sema.NumLeafScopes 13,105,157 13,105,435 278 0.0%
Sema.NumTypesDeserialized 17,064,348 17,068,222 3,874 0.02%
Sema.NumTypesValidated 1,827,289 1,827,299 10 0.0%
Sema.NumUnloadedLazyIterableDeclContexts 4,754,784 4,755,511 727 0.02%
Sema.OverriddenDeclsRequest 8,123,743 8,131,740 7,997 0.1%
Sema.PropertyDelegateBackingPropertyInfoRequest 277,026 277,026 0 0.0%
Sema.PropertyDelegateBackingPropertyTypeRequest 279,543 279,543 0 0.0%
Sema.PropertyDelegateTypeInfoRequest 0 0 0 0.0%
Sema.RequirementRequest 68,749 68,751 2 0.0%
Sema.RequirementSignatureRequest 71,493 71,383 -110 -0.15%
Sema.SelfBoundsFromWhereClauseRequest 6,653,395 6,659,885 6,490 0.1%
Sema.SetterAccessLevelRequest 159,441 159,441 0 0.0%
Sema.StructuralTypeRequest 0 0 0 0.0%
Sema.SuperclassDeclRequest 88,805 88,707 -98 -0.11%
Sema.SuperclassTypeRequest 38,570 38,570 0 0.0%
Sema.TypeDeclsFromWhereClauseRequest 33,155 33,151 -4 -0.01%
Sema.USRGenerationRequest 13,019,632 13,042,348 22,716 0.17%
Sema.UnderlyingTypeDeclsReferencedRequest 179,315 179,241 -74 -0.04%

Release

release brief

Regressed (0)
name old new delta delta_pct
Improved (0)
name old new delta delta_pct
Unchanged (delta < 1.0% or delta < 100.0ms) (3)
name old new delta delta_pct
Frontend.NumInstructionsExecuted 31,177,247,764,408 31,181,644,537,711 4,396,773,303 0.01%
LLVM.NumLLVMBytesOutput 994,355,778 994,359,634 3,856 0.0%
time.swift-driver.wall 5432.9s 5425.2s -7.7s -0.14%

release detailed

Regressed (0)
name old new delta delta_pct
Improved (0)
name old new delta delta_pct
Unchanged (delta < 1.0% or delta < 100.0ms) (22)
name old new delta delta_pct
AST.NumImportedExternalDefinitions 233,224 233,224 0 0.0%
AST.NumLoadedModules 16,875 16,875 0 0.0%
AST.NumTotalClangImportedEntities 803,549 803,549 0 0.0%
IRModule.NumIRBasicBlocks 3,974,127 3,974,127 0 0.0%
IRModule.NumIRFunctions 1,791,176 1,791,176 0 0.0%
IRModule.NumIRGlobals 1,971,430 1,971,430 0 0.0%
IRModule.NumIRInsts 36,356,472 36,356,472 0 0.0%
IRModule.NumIRValueSymbols 3,494,151 3,494,151 0 0.0%
LLVM.NumLLVMBytesOutput 994,355,778 994,359,634 3,856 0.0%
SILModule.NumSILGenFunctions 762,829 762,829 0 0.0%
SILModule.NumSILOptFunctions 1,020,342 1,020,342 0 0.0%
Sema.NumConformancesDeserialized 2,358,078 2,358,078 0 0.0%
Sema.NumConstraintScopes 17,909,448 17,909,448 0 0.0%
Sema.NumDeclsDeserialized 6,346,805 6,346,805 0 0.0%
Sema.NumDeclsValidated 1,244,363 1,244,363 0 0.0%
Sema.NumFunctionsTypechecked 489,129 489,129 0 0.0%
Sema.NumGenericSignatureBuilders 201,154 201,154 0 0.0%
Sema.NumLazyGenericEnvironments 1,301,356 1,301,356 0 0.0%
Sema.NumLazyGenericEnvironmentsLoaded 22,517 22,517 0 0.0%
Sema.NumLazyIterableDeclContexts 809,170 809,170 0 0.0%
Sema.NumTypesDeserialized 3,318,861 3,318,861 0 0.0%
Sema.NumTypesValidated 751,167 751,167 0 0.0%

@atrick
Copy link
Contributor Author

atrick commented May 12, 2019

@swift-ci smoke test

@gottesmm
Copy link
Contributor

@atrick Can you put in a scale unit test for this with the test program? I think I remember how to do it if you do not.

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

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

Overall looks ok. Some small asks.

StructDecl *getStructDecl() const {
auto s = getOperand()->getType().getStructOrBoundGenericStruct();
NominalTypeDecl *getParentDecl() const {
auto s = getOperand(0)->getType().getNominalOrBoundGenericNominal();
Copy link
Contributor

Choose a reason for hiding this comment

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

Meta question. Should the base type of this be some sort of unary instruction base or something? Then getOperand() is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing that out; there should be an assert. There was actually a reason I didn't make this a subclass ofUnary Instruction.
d98cf0c

@atrick
Copy link
Contributor Author

atrick commented May 13, 2019

@gottesmm you can see review feedback in separate commits. I think that's easier to keep track of. I'm still working on the scale test. It's tricky because there's no sil-opt support yet. So I can count the number of calls to getStoredProperties, but that's probably still quadratic somewhere in the compiler. I can count the number of times SIL calls it from the new API, but that's useless as a regression test.

atrick added 3 commits May 13, 2019 16:54
The field's ordinal value is used by the Projection abstraction, which is
the basis of efficiently comparing and sorting access paths in SIL. It must
be cached before it is used by any SIL passes, including the verifier, or it
causes widespread quadratic complexity.

Fixes <rdar://problem/50353228> Swift compile time regression with optimizations enabled

In production code, a file that was taking 40 minutes to compile now
takes 1 minute, with more than half of the time in LLVM.

Here's a short script that reproduces the problem. It used to take 30s
and now takes 0.06s:

// swift genlazyinit.swift > lazyinit.sil
// sil-opt ./lazyinit.sil --access-enforcement-opts

var NumProperties = 300

print("""
      sil_stage canonical

      import Builtin
      import Swift
      import SwiftShims

      public class LazyProperties {
      """)

for i in 0..<NumProperties {
  print("""
          //  public lazy var i\(i): Int { get set }
          @_hasStorage @_hasInitialValue final var __lazy_storage__i\(i): Int? { get set }
        """)
}

print("""
      }

     // LazyProperties.init()
     sil @$s4lazy14LazyPropertiesCACycfc : $@convention(method) (@owned LazyProperties) -> @owned LazyProperties {
     bb0(%0 : $LazyProperties):
       %enum = enum $Optional<Int>, #Optional.none!enumelt
     """)

for i in 0..<NumProperties {
  let adr = (i*4) + 2
  let access = adr + 1
  print("""
          %\(adr) = ref_element_addr %0 : $LazyProperties, #LazyProperties.__lazy_storage__i\(i)
          %\(access) = begin_access [modify] [dynamic] %\(adr) : $*Optional<Int>
          store %enum to %\(access) : $*Optional<Int>
          end_access %\(access) : $*Optional<Int>
        """)
}

print("""
        return %0 : $LazyProperties
      } // end sil function '$s4lazy14LazyPropertiesCACycfc'
      """)
I'm not sure how anyone debugs these tests otherwise.
Adds a NumStoredPropertiesQueries stat.

Adds a test case for an increasing number of lazy stored class
properties. Each property requires a formal access within the
initializer. This would manifest as cubic behavior in
AccessEnforcementOpts, which scales as O(1.5) in the above stat.
@atrick
Copy link
Contributor Author

atrick commented May 14, 2019

@gottesmm I squashed the review feedback and added the scale test so it's ready to merge.

@atrick
Copy link
Contributor Author

atrick commented May 14, 2019

@swift-ci test.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 161a6721920f6b04bbf609fb3ea36644638a13be

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 161a6721920f6b04bbf609fb3ea36644638a13be

@atrick atrick merged commit 22e5c55 into swiftlang:master May 14, 2019
@atrick atrick deleted the cache-field-number branch May 14, 2019 19:36
atrick added a commit that referenced this pull request May 14, 2019
[5.1] Merge pull request #24717 from atrick/cache-field-number
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.

4 participants