Skip to content

[stdlib] Resolving some FIXME comments on Set type. #20631

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 10 commits into from
Dec 15, 2018

Conversation

LucianoPAlmeida
Copy link
Contributor

This is a very simple PR resolving some FIXME comments on Set.swift as a very first contribution :))

@natecook1000
Copy link
Member

Welcome to the Swift open source project, @LucianoPAlmeida! This looks great. Could you change your additions to use 2-space indentation? Once that's done we can kick off a test.

@LucianoPAlmeida
Copy link
Contributor Author

For sure @natecook1000, fixed indentation :))

@xwu
Copy link
Collaborator

xwu commented Nov 17, 2018

@swift-ci please smoke test

@airspeedswift
Copy link
Member

@swift-ci please benchmark

@swift-ci
Copy link
Contributor

Build comment file:

Performance: -Onone

TEST OLD NEW DELTA RATIO
Improvement
ObjectiveCBridgeFromNSSetAnyObjectForced 5142 4341 -15.6% 1.18x
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
--------------

@LucianoPAlmeida
Copy link
Contributor Author

Hey guys :))
smoke test pass, but do we need to run validation test here too?

@@ -712,7 +712,8 @@ extension Set: SetAlgebra {
@inlinable
public func isSubset<S: Sequence>(of possibleSuperset: S) -> Bool
where S.Element == Element {
// FIXME(performance): isEmpty fast path, here and elsewhere.
Copy link
Collaborator

Choose a reason for hiding this comment

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

As noted in the comment, there are places "elsewhere" that could use an isEmpty fast path. Before deleting the fixme, it'd be important to identify where those other places are and either implement the fast path or add the fixme comment there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For sure @xwu 👍
I've checked the methods on Set and added it in all places that I could find it would be possible :))

Copy link
Collaborator

Choose a reason for hiding this comment

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

I’m not so sure you got everything. Wouldn’t isSuperset benefit from an isEmpty fast path?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've check this isSuperset, but didn't think it has benefit, is an interesting case because just because we have two cases there

let s: Set<String> = []

print(s.isSuperset(of: [])) //false
print(s.isSuperset(of: ["A", "B"])) //true

So to add an isEmpty fast path will require, check also possibleSubset for empty too, but since it is a arbitrary sequence it doens't have an isEmpty and as far as I know we could not do this check on underestimatedCount.

Also looking to the implementation(below) no matter what is the size of possibleSubset if self is empty it will return on the first iteration because contains will return false. So there's no performance problem because it will not iterate unnecessarily. Basically, that is the reason I think is not a benefit :))

public func isSuperset<S: Sequence>(of possibleSubset: __owned S) -> Bool
    where S.Element == Element {
    for member in possibleSubset {
      if !contains(member) {
        return false
      }
    }
    return true
  }

Copy link
Contributor

Choose a reason for hiding this comment

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

Turns out there was more "elsewhere": isStrictSubset and isStrictSuperset with Sequence parameter could probably also benefit form optimizations for isEmpty cases... see #24156 (comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@palimondo Sorry I miss those 😅, I'll take a look and I can patch up a PR later today :))

Copy link
Contributor

@palimondo palimondo May 7, 2019

Choose a reason for hiding this comment

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

No problem. Please don't go in there now, #21300 is about to drastically change how that's handled and I'm already looking into how to handle isEmpty cases on top of it… it only pays the cheap creation of _Bitset(capacity: self.bucketCount) and will bail out of the Sequence consuming loop ASAP.

I just couldn't resist noting how @xwu's pedantry usually turns out to be right, even though it feels pretty annoying to be on the receiving end of it (speaking from experience 😉).

@natecook1000
Copy link
Member

@swift-ci Please smoke test

@LucianoPAlmeida
Copy link
Contributor Author

Only some people can trigger tests, right? Can someone please trigger benchmark here :))

@xwu
Copy link
Collaborator

xwu commented Dec 13, 2018

@swift-ci Benchmark

@xwu
Copy link
Collaborator

xwu commented Dec 13, 2018

@swift-ci Please benchmark?

@swift-ci
Copy link
Contributor

Build comment file:

Performance: -O

TEST OLD NEW DELTA RATIO
Regression
CountAlgoString 1550 1690 +9.0% 0.92x
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: 8-Core Intel Xeon E5
  Processor Speed: 3 GHz
  Number of Processors: 1
  Total Number of Cores: 8
  L2 Cache (per Core): 256 KB
  L3 Cache: 25 MB
  Memory: 16 GB
--------------

@LucianoPAlmeida
Copy link
Contributor Author

Checking the benchmark/single-source/SetTest.swift found that isDisjoint was not being benchmarked.
Also I've added empty set cases for benchmark too :))

@natecook1000
Copy link
Member

@swift-ci Please benchmark

@natecook1000
Copy link
Member

@swift-ci Please smoke test

@swift-ci
Copy link
Contributor

Build comment file:

Performance: -O

TEST MIN MAX MEAN MAX_RSS
Added
SetIsDisjointBox0 68002 68602 68333
SetIsDisjointBox25 5 5 5
SetIsDisjointEmptyBox0 1 1 1
SetIsDisjointEmptyInt0 1 1 1
SetIsDisjointInt0 29345 29514 29426
SetIsDisjointInt100 1 1 1
SetIsDisjointInt25 2 2 2
SetIsDisjointInt50 2 2 2
SetIsSubsetEmptyInt0 220 221 220
SetSubtractingEmptyBox0 0 0 0
SetSubtractingEmptyInt0 0 0 0

Code size: -O

TEST OLD NEW DELTA RATIO
Regression
SetTests.o 64415 74727 +16.0% 0.86x

Performance: -Osize

TEST MIN MAX MEAN MAX_RSS
Added
SetIsDisjointBox0 84710 85258 84913
SetIsDisjointBox25 7 7 7
SetIsDisjointEmptyBox0 1 1 1
SetIsDisjointEmptyInt0 1 1 1
SetIsDisjointInt0 30257 30338 30309
SetIsDisjointInt100 2 2 2
SetIsDisjointInt25 3 3 3
SetIsDisjointInt50 3 3 3
SetIsSubsetEmptyInt0 227 233 229
SetSubtractingEmptyBox0 0 0 0
SetSubtractingEmptyInt0 0 0 0

Code size: -Osize

TEST OLD NEW DELTA RATIO
Regression
SetTests.o 57759 66791 +15.6% 0.86x

Performance: -Onone

TEST OLD NEW DELTA RATIO
Improvement
DictionaryKeysContainsCocoa 78 54 -30.8% 1.44x
Added
SetIsDisjointBox0 317297 317781 317579
SetIsDisjointBox25 26 26 26
SetIsDisjointEmptyBox0 5 5 5
SetIsDisjointEmptyInt0 5 5 5
SetIsDisjointInt0 135109 135375 135209
SetIsDisjointInt100 9 10 9
SetIsDisjointInt25 13 13 13
SetIsDisjointInt50 13 13 13
SetIsSubsetEmptyInt0 499 503 501
SetSubtractingEmptyBox0 1 1 1
SetSubtractingEmptyInt0 1 1 1
Benchmark Check Report
⚠️🔤 SetIsDisjointEmptyBox0 name is composed of 5 words.
Split SetIsDisjointEmptyBox0 name into dot-separated groups and variants. See http://bit.ly/BenchmarkNaming
⛔️⏱ SetIsDisjointInt0 execution took at least 29103 μs.
Decrease the workload of SetIsDisjointInt0 by a factor of 32 (100), to be less than 1000 μs.
⚠️🔤 SetIsDisjointEmptyInt0 name is composed of 5 words.
Split SetIsDisjointEmptyInt0 name into dot-separated groups and variants. See http://bit.ly/BenchmarkNaming
⛔️⏱ SetIsDisjointBox0 execution took at least 67994 μs.
Decrease the workload of SetIsDisjointBox0 by a factor of 128 (100), to be less than 1000 μs.
⚠️🔤 SetIsSubsetEmptyInt0 name is composed of 5 words.
Split SetIsSubsetEmptyInt0 name into dot-separated groups and variants. See http://bit.ly/BenchmarkNaming
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
--------------

Copy link
Member

@lorentey lorentey left a comment

Choose a reason for hiding this comment

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

Nice work; thanks for adding those missing benchmarks!

The benchmark report pointed out two scaling issues (also noted in my comment above); other than those, this looks ready to merge!

setUpFunction: { blackHole([setAB, setCD]) }),
BenchmarkInfo(
name: "SetIsDisjointBox0",
runFunction: { n in run_SetIsDisjointBox(setOAB, setOCD, true, 5000 * n) },
Copy link
Member

@lorentey lorentey Dec 15, 2018

Choose a reason for hiding this comment

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

0-overlap sets are the most expensive for isDisjoint(with:) — these two benchmark cases (SetIsDisjointInt0, SetIsDisjointBox0) should use the same factor of 50 as the others.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just fixed 👍
Thank's for the review @lorentey :))

@natecook1000
Copy link
Member

@swift-ci Please smoke test

@natecook1000 natecook1000 merged commit 2bc5623 into swiftlang:master Dec 15, 2018
@palimondo
Copy link
Contributor

For the future reference: new performance tests should land in a separate commit from the actual performance work, so that they can demonstrate the expected improvements. Now we only see the after effect in the Added section of the benchmark results.

I'm working on a fix to the loop multipliers that are too low for most of the newly introduced benchmarks and improvement to the benchmark validation to prevent this kind of mistake in PR #21717.

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.

7 participants