This is an archive of the discontinued LLVM Phabricator instance.

Initialize much of AssemblyWriter lazily.
Needs ReviewPublic

Authored by jlebar on Mar 1 2016, 5:39 PM.

Details

Summary

The AssemblyWriter constructor is in the critical path of
Value::operator<<, which in turn is in the critical path of most debug
messages. It currently scans the entire module multiple times, even
though most of that work is usually unnecessary. This change makes us
lazier.

With this change, I can compile a big Thrust file with
-debug-only=inline in a few minutes. Before, I gave up after 30m.

Diff Detail

Event Timeline

jlebar updated this revision to Diff 49571.Mar 1 2016, 5:39 PM
jlebar retitled this revision from to Initialize much of AssemblyWriter lazily..
jlebar updated this object.
jlebar added a reviewer: majnemer.
jlebar added a subscriber: llvm-commits.
rafael added inline comments.
lib/IR/AsmWriter.cpp
426

Why the fancy decltype? Can this be just

TypeFinder getNamedTypes() {....

?

jlebar updated this revision to Diff 50350.Mar 10 2016, 2:07 PM

Nix unnecessary trailing return + decltype.

lib/IR/AsmWriter.cpp
426

I have no idea why I did that. If I should use the fancy thing anywhere, it should be on the function below, which at least has an ugly return type.

Anyway, fixed, thank you.

timshen added inline comments.Mar 30 2016, 6:01 PM
lib/IR/AsmWriter.cpp
416

Do you think it's better to create a Lazy<T> template type, which has an Optional<T>, but automatically checks & value-initializes the underlying T on operator->()/operator*() ? It seems able to reduce the users' mind burden compared to using raw Optional.

timshen edited edge metadata.Mar 31 2016, 11:32 AM

Obviously if I add an inline comment without a non-inline comment, I'm still shown as "review requested". Add this comment to make Phabricator happy.

jlebar updated this revision to Diff 52309.Mar 31 2016, 4:22 PM
jlebar edited edge metadata.

Use Lazy<T> in AssemblyWriter patch.

lib/IR/AsmWriter.cpp
416

Hm. If we go this route, I think we don't just want to value-initialize T -- that opens us up to a bug where you accidentally use one of these containers before it's been filled with data.

Now, a Lazy<T> that takes an std::function and uses *that* to initialize T...that's interesting. I did it, but I am not sure it's clearer.

Have a look, see what you think? I saved my state so we can go back no problem.

inline comments.

lib/IR/AsmWriter.cpp
82

Man, by last time I looked at libstdc++, it's like 48 bytes on 64-bit machines...

Even if Lazy keeps track of the raw lambda, the lambda may still capture stuff and have memory footprint.

Now I think it's a bad idea. It's as bad as that dtor doesn't have parameters so each container, which may be in a parent container, needs to keep its allocator.

Sorry, I think the Optional version is better. :(

BTW, how hard is it to make the user side lazy, rather the library side?

jlebar added inline comments.Mar 31 2016, 4:51 PM
lib/IR/AsmWriter.cpp
82

Man, by last time I looked at libstdc++, it's like 48 bytes on 64-bit machines...

Eh, I don't think this is a big deal, compared to all the other work we're doing here. :)

Even if Lazy keeps track of the raw lambda, the lambda may still capture stuff and have memory footprint.

Indeed, but these lambdas don't, so we're ok in that respect!

It's as bad as that dtor doesn't have parameters so each container, which may be in a parent container, needs to keep its allocator.

:) Here I'm much more concerned about readability. Like, we're making this *way* faster in any case; the extra few bytes or whatever isn't a big deal.

BTW, how hard is it to make the user side lazy, rather the library side?

I'm not sure what you mean; can you give me an example?

timshen added inline comments.Mar 31 2016, 4:57 PM
lib/IR/AsmWriter.cpp
82

:) Here I'm much more concerned about readability. Like, we're making this *way* faster in any case; the extra few bytes or whatever isn't a big deal.

If you don't think extra is a big deal, go for it :). But still be aware that the moment when the underlying object T gets initialized is relatively hard to track down (that's the whole point, isn't it ;).

I'm not sure what you mean; can you give me an example?

By saying "user" I mean "Value::operator<<", and library is "AssemblyWriter". Is there a way to keep an AssemblyWriter instance in Value::operator<<'s context (Module?), so that you don't have to change AssemblyWriter?

jlebar updated this revision to Diff 52311.Mar 31 2016, 4:58 PM

Roll back to previous version.

Is there a way to keep an AssemblyWriter instance in Value::operator<<'s context (Module?), so that you don't have to change AssemblyWriter?

Ah. That would be nice. But I think it's hard because AssemblyWriter assumes the module doesn't change.

I don't have more comments. Should I just stamp this patch?

lib/IR/AsmWriter.cpp
461

Optional:

LazyNamedTypes.erase(
  std::remove_if(LazyNamedTypes.begin(), LazyNamedTypes.end(),
    [&](...) { ... }),
  LazyNamedTypes.end());

?

I don't know if you like this half-functional style or pure imperative style.

Should I just stamp this patch?

If you're comfortable doing so!

lib/IR/AsmWriter.cpp
461

Normally I like that, but here we're doing so much other stuff (building up LazyNumberedTypes), I think it's probably clearer as-is.

Actually I don't feel comfortable to stamp it - I've never seen this code before; but LGTM. :)

espindola edited reviewers, added: espindola; removed: rafael.Mar 15 2018, 10:55 AM