Open weissi opened 6 years ago
@swift-ci create
@swift-ci create
This is a huge language-design request, but one we've been interested in in the past. It's not just a matter of adding opt-remarks around compiler-inserted allocations, though.
@belkadan Is this really a language design request? To me this is a reasonable, incremental request for a build warning that is allowed to be noisy. Why should the ultimate "keep things on the stack language feature" stop us from exposing this information to our users?
Additionally, regardless of whether or not we expose this to users I think for compiler performance engineers this would be really useful in terms of quickly understanding where the optimizer fails without having to read SIL.
In terms of implementation @atrick and I spoke about this a little bit. The main interesting part is that we may run function passes many times with interleaving module passes in the SILPassManager. To implement this one would thus need to have some sort of way invalidating opt remarks from previous runs. There are a bunch of ways you could do this, but I imagine the simplest way is to have an analysis that invalidates opt remarks when a pass begins. The reason to use an analysis is that analyses are the standard way besides the IR to communicate information in between SILPasses. Then we could have a pass that at the end of the pipeline that consumes the analysis and emits the final opt remarks.
I think the spirit of this bug is: what information can the compiler provide without broaching language design? In the past, I've considered calls to malloc to be insignificant relative to other performance issues. But now we have developers attempting to minimize allocations using whatever unsafe APIs are at there disposal. Since there aren't language guarantees, they need to easily detect when, where, and hopefully why their tuning fails or regresses. I think this is just a expert level testing/diagnostic feature.
This is incredibly language design-y. At first glance, you want to know
whether you're using a language construct that requires malloc (existentials, classes, escaping closures with captures)
whether you're using a runtime feature that might call malloc (conformance lookup? ObjC message send?)
but the real problem is that you also need to know whether anything you call is going to allocate. If you don't have that, then the compiler won't tell you that your Swift.Set is also partly on the heap. That's ultimately an effects system, the same as "pure".
I don't think this means that we can't do anything here, but it does mean that it's not really a question about "where the optimizer fails". That's not the question users are asking. (If it helps us improve the compiler, it's worth adding, but not advertising.)
(Note that I'm assuming generics and non-escaping closures ought to require no extra allocations even without optimization. We're nearly there!)
Jordan. That is not what is being asked for here. You are trying to devise a solution that prevents any/all allocations. That would require an effects system. That is specifically not what we are trying to do here.
Rather, I am focused on the specific types of allocations that Johannes is hitting. Specifically, if I understand correctly, the allocations that Johannes is talking about are things where for some reason we are not stack promoting a class or a box. Using this opt-remark tool, Johannes can provide the performance team with better data on this and have knowledge if the optimizer failed.
That being said, I just pinged Johannes to double check the correctness of my assertion that the allocations are mostly classes that we are not stack promoting for some reason.
@belkadan/@gottesmm yes, I was after what Michael suggested: I'd like to get a warning/error for things that can be promoted to the stack but don't in a particular function.
IntelliJ apparently can at least show 'allocation in loop': https://twitter.com/tagir_valeev/status/1166606424329396224
Attachment: Download
Additional Detail from JIRA
| | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, LanguageFeatureRequest | |Assignee | None | |Priority | Medium | md5: 2a4c1feea37249b97a389c1143f2420dIssue Description:
Accidental heap allocations are a major source of frustration when writing high-performance Swift. For SwiftNIO we had to create integration tests which literally count the number of mallocs (and frees) by hooking the functions. In CI we then compare those numbers against a baseline we set (by exporting environment variables).
This is quite cumbersome and a lot of work as the limits depend at least on the exact Swift version and also the OS. Also the tests are somewhat costly to write. In most cases the tests just exist to make sure that in certain areas of SwiftNIO we don't cause any new heap allocations. But even then it only makes sure we don't regress.
What would be incredibly handy would be some kind of mechanism that would get the compiler to warn/error of a heap allocation is happening in a certain bit of code. I don't really know yet how that should look in the source language but maybe someone has an idea.
Some links:
@gottesmm pointed out the already existing opt-remark system is does similar things:
https://github.com/apple/swift/blob/ff9c86552e6c7ed62ec49c06ed7dff57e79a754e/test/Driver/opt-remark.swift
https://github.com/apple/swift/blob/master/lib/SILOptimizer/Transforms/GenericSpecializer.cpp#L75
here is a description (and the code) for the integration test that does allocation counting in SwiftNIO:
https://github.com/apple/swift-nio/tree/master/IntegrationTests/tests_04_performance/test_01_resources
and here's a sample output from CI: