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

Add IR-to-Objectcode caching with `-ir2obj-cache=<cache dir>`. #1572

Merged
merged 1 commit into from Jun 23, 2016

Conversation

Projects
None yet
2 participants
@JohanEngelen
Member

JohanEngelen commented Jun 20, 2016

This adds a caching mechanism such that compiling the same source twice should be faster as it will use the previously generated object file.
The caching granularity is per LLVM module.

With -ir2obj-cache=<cache dir>, LDC does:

  1. Parse/analyse/etc the code
  2. Generate LLVM IR
    3. Create hash of LLVM IR (NEW)
    4. Do hash lookup in cachedir. (NEW)
    If hash found: output symlink to cached object file and exit. (NEW)
  3. Optimization
  4. Output object file
    7. Store hash-objectfile in cache dir. (NEW)

Machine codegen takes a lot of time, so this caching results in a significant speed up (~65sec -> ~39sec on testcase). Generating the hash is done by hashing the raw bitcode output from LLVM. This is not for free and takes quite some time: it takes time to generate a 200MB bitcode file, I think the hash time itself is not noticable. Future work could try to get a (unique) hash quicker through some other means.

@JohanEngelen

This comment has been minimized.

Show comment
Hide comment
@JohanEngelen

JohanEngelen Jun 20, 2016

Member

now compiles with LLVM != 3.8

Member

JohanEngelen commented Jun 20, 2016

now compiles with LLVM != 3.8

driver/cl_options.cpp
@@ -187,6 +187,10 @@ static cl::opt<bool, true> unittest("unittest",
cl::desc("Compile in unit tests"),
cl::location(global.params.useUnitTests));
+cl::opt<std::string>
+ ir2objCacheDir("ir2obj-cache", cl::desc("Write&read LLVM IR to object code cache in <cache dir> directory"),

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Maybe something like "Use <cache dir> to cache object files for specific LLVM IR modules"? The fact ir2obj-cache has something to do with an IR to object code cache is fairly obvious.

@klickverbot

klickverbot Jun 22, 2016

Member

Maybe something like "Use <cache dir> to cache object files for specific LLVM IR modules"? The fact ir2obj-cache has something to do with an IR to object code cache is fairly obvious.

This comment has been minimized.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Slight improvement "Use to cache object files for specific LLVM IR modules"?

How about the flag itself?

Edit: lol, I guess like you typed <cache dir> too in your comment, but somehow github removes it.
"Use <cache dir> to cache object files for specific LLVM IR modules"

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Slight improvement "Use to cache object files for specific LLVM IR modules"?

How about the flag itself?

Edit: lol, I guess like you typed <cache dir> too in your comment, but somehow github removes it.
"Use <cache dir> to cache object files for specific LLVM IR modules"

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

lol indeed – that must be a HTML tag escaper gone crazy or something along these lines. I edited the old comment to work around it.

As for the flag itself, if you advertise the caching functionality as "IR to object file cache", then I suppose it is about as clear as it gets. I've been trying to think about alternative names, but everything involving "codegen" somehow seems to be too unspecific.

@klickverbot

klickverbot Jun 22, 2016

Member

lol indeed – that must be a HTML tag escaper gone crazy or something along these lines. I edited the old comment to work around it.

As for the flag itself, if you advertise the caching functionality as "IR to object file cache", then I suppose it is about as clear as it gets. I've been trying to think about alternative names, but everything involving "codegen" somehow seems to be too unspecific.

+
+namespace {
+
+/// A raw_ostream that creates a hash of what is written to it.

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Is this from LLVM? Should probably point that out because of the slightly different license.

@klickverbot

klickverbot Jun 22, 2016

Member

Is this from LLVM? Should probably point that out because of the slightly different license.

This comment has been minimized.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Hm, actually pretty much only the comment is from LLVM.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Hm, actually pretty much only the comment is from LLVM.

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Ah, okay – I just vaguely remembered reading a similar comment in the upstream ThinLTOCodeGenerator stuff.

@klickverbot

klickverbot Jun 22, 2016

Member

Ah, okay – I just vaguely remembered reading a similar comment in the upstream ThinLTOCodeGenerator stuff.

+
+ IF_LOG Logger::println("SymLink output to cached object file: %s -> %s",
+ objectFile.str().c_str(), cacheFile.c_str());
+ if (llvm::sys::fs::create_link(cacheFile.c_str(), objectFile)) {

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

What does this actually do on Windows?

@klickverbot

klickverbot Jun 22, 2016

Member

What does this actually do on Windows?

This comment has been minimized.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Windows also has symlinks. According to LLVM's documentation:
"The link may be a soft or a hard link, depending on the platform. The caller may not assume which one. Currently on windows it creates a hard link since soft links require extra privileges. On unix, it creates a soft link since hard links don't work on SMB file systems."

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Windows also has symlinks. According to LLVM's documentation:
"The link may be a soft or a hard link, depending on the platform. The caller may not assume which one. Currently on windows it creates a hard link since soft links require extra privileges. On unix, it creates a soft link since hard links don't work on SMB file systems."

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

I suppose that assumes that nobody is trying to build on FAT32 or weird network shares? As long as we fail with a clear error message in such situations, this isn't going to be an issue, though.

@klickverbot

klickverbot Jun 22, 2016

Member

I suppose that assumes that nobody is trying to build on FAT32 or weird network shares? As long as we fail with a clear error message in such situations, this isn't going to be an issue, though.

This comment has been minimized.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Hmm... perhaps add the word "symbolic link" or something to the error message?
Before, I used llvm::sys::fs::copy, but then I "measured" that for Weka's huge object files that actually takes more than a second on my PC and was happy to find LLVM's (semi-?)cross-platform create_link. :)

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

Hmm... perhaps add the word "symbolic link" or something to the error message?
Before, I used llvm::sys::fs::copy, but then I "measured" that for Weka's huge object files that actually takes more than a second on my PC and was happy to find LLVM's (semi-?)cross-platform create_link. :)

+
+
+// FIRST: Use IR-to-Object cache in {{.*}}cachedirectory
+// Don't check whether the object is in the cache on the first run, because if this test is ran twice the cache will already be there.

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Can't we just nuke the cache directory first?

@klickverbot

klickverbot Jun 22, 2016

Member

Can't we just nuke the cache directory first?

This comment has been minimized.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

I don't know how to remove a directory with a cross-platform command and without errorring if the directory does not exist.

@JohanEngelen

JohanEngelen Jun 22, 2016

Member

I don't know how to remove a directory with a cross-platform command and without errorring if the directory does not exist.

driver/toobj.cpp
// There is no integrated assembler on AIX because XCOFF is not supported.
// Starting with LLVM 3.5 the integrated assembler can be used with MinGW.
bool const assembleExternally =
global.params.output_o &&
(NoIntegratedAssembler ||
global.params.targetTriple->getOS() == llvm::Triple::AIX);
+ // Use cached object code if possible
+ bool useIRToObjCache = !opts::ir2objCacheDir.empty();

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Very, very minor nitpick: I'd go for consistent rendition as "useIR2ObjCache", if your are using it for the user-facing parts (there is, of course, some precedent for these slightly cheesy names in LLVM – mem2reg, etc.).

@klickverbot

klickverbot Jun 22, 2016

Member

Very, very minor nitpick: I'd go for consistent rendition as "useIR2ObjCache", if your are using it for the user-facing parts (there is, of course, some precedent for these slightly cheesy names in LLVM – mem2reg, etc.).

driver/ir2obj_cache.h
@@ -0,0 +1,29 @@
+//===-- driver/ir2obj-cache.h -----------------------------------*- C++ -*-===//

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

- vs. _. ;)

@klickverbot

klickverbot Jun 22, 2016

Member

- vs. _. ;)

driver/ir2obj_cache.cpp
+// that file is used and machine code gen is skipped entirely. If the cache
+// doesn't contain that file, machine codegen happens as normal and the object
+// code is added to the cache.
+// The goal is to speed up successive builds with only minor changes.

This comment has been minimized.

@klickverbot

klickverbot Jun 22, 2016

Member

Maybe "successive builds of a big codebase with several object files after minor changes" or something like that, to clarify that this doesn't apply to -singleobj (or equivalent) builds. The fact that there is a comment in the first place is of course quite a lot for LDC already, but I think it would make sense to be clear about this "limitation" (i.e. us not caching module fragments) in the user documentation and initial announcements.

@klickverbot

klickverbot Jun 22, 2016

Member

Maybe "successive builds of a big codebase with several object files after minor changes" or something like that, to clarify that this doesn't apply to -singleobj (or equivalent) builds. The fact that there is a comment in the first place is of course quite a lot for LDC already, but I think it would make sense to be clear about this "limitation" (i.e. us not caching module fragments) in the user documentation and initial announcements.

@JohanEngelen

This comment has been minimized.

Show comment
Hide comment
@JohanEngelen

JohanEngelen Jun 23, 2016

Member

@klickverbot I think I've addressed all your comments.

Member

JohanEngelen commented Jun 23, 2016

@klickverbot I think I've addressed all your comments.

@klickverbot

This comment has been minimized.

Show comment
Hide comment
@klickverbot

klickverbot Jun 23, 2016

Member

I suppose there is little harm in adding this as an experimental feature (maybe add "(experimental)" in place of "for faster recompilation of unchanged IR code."?).

The reason why I'm a bit cautious is that it seems like we might want to do something more fully-featured in the near future like what ThinLTO in Clang/LLVM are slowly moving towards. It would be a pity to have to remove/rework the command line flag then, breaking people's stuff. Although, thinking of it, this should be reasonably painless to keep maintained on the side due to its relatively small size. And if the worst comes to the worst, we could still just ignore the flag, which would only make builds slower, not break them.

So, just ignore the last stream-of-consciousness paragraph of doubts – feel free to merge, although I would make sure that the feature is advertised as experimental sufficiently clearly for the time being.

Member

klickverbot commented Jun 23, 2016

I suppose there is little harm in adding this as an experimental feature (maybe add "(experimental)" in place of "for faster recompilation of unchanged IR code."?).

The reason why I'm a bit cautious is that it seems like we might want to do something more fully-featured in the near future like what ThinLTO in Clang/LLVM are slowly moving towards. It would be a pity to have to remove/rework the command line flag then, breaking people's stuff. Although, thinking of it, this should be reasonably painless to keep maintained on the side due to its relatively small size. And if the worst comes to the worst, we could still just ignore the flag, which would only make builds slower, not break them.

So, just ignore the last stream-of-consciousness paragraph of doubts – feel free to merge, although I would make sure that the feature is advertised as experimental sufficiently clearly for the time being.

@JohanEngelen

This comment has been minimized.

Show comment
Hide comment
@JohanEngelen

JohanEngelen Jun 23, 2016

Member

ThinLTO is definitely something interesting to look into for speeding up builds too.
For future reference: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

I'll add "(experimental)" and merge. Perhaps someone can work-out a faster way of obtaining a unique ID for the IR, which would then break existing hashes. If people like to store them for a long time and have them be reusable, we can tell them: "hey, it says 'experimental'!" ;-).

Member

JohanEngelen commented Jun 23, 2016

ThinLTO is definitely something interesting to look into for speeding up builds too.
For future reference: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

I'll add "(experimental)" and merge. Perhaps someone can work-out a faster way of obtaining a unique ID for the IR, which would then break existing hashes. If people like to store them for a long time and have them be reusable, we can tell them: "hey, it says 'experimental'!" ;-).

@JohanEngelen JohanEngelen merged commit 4d41adc into ldc-developers:master Jun 23, 2016

0 of 3 checks passed

ci/circleci CircleCI is running your tests
Details
continuous-integration/appveyor/pr Waiting for AppVeyor build to complete
Details
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details

@JohanEngelen JohanEngelen deleted the JohanEngelen:IR-cache branch Jun 23, 2016

@klickverbot

This comment has been minimized.

Show comment
Hide comment
@klickverbot

klickverbot Jun 23, 2016

Member

Perhaps someone can work-out a faster way of obtaining a unique ID for the IR, which would then break existing hashes.

The nice thing about caches is that you can just throw existing contents without compromising correctness – just performance! ;) (Assuming no collisions, of course.)

Member

klickverbot commented Jun 23, 2016

Perhaps someone can work-out a faster way of obtaining a unique ID for the IR, which would then break existing hashes.

The nice thing about caches is that you can just throw existing contents without compromising correctness – just performance! ;) (Assuming no collisions, of course.)

@klickverbot

This comment has been minimized.

Show comment
Hide comment
@klickverbot

klickverbot Jun 23, 2016

Member

For future reference: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

Ah, nice, I hadn't noticed that they have finally written up their progress (and that they are going to be shipping this in Xcode 8 Beta already).

We might need to invest some work into optimising it for the debug build use case, though – at least from the blog post, it sounds like they are exclusively working on release builds so far.

Member

klickverbot commented Jun 23, 2016

For future reference: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

Ah, nice, I hadn't noticed that they have finally written up their progress (and that they are going to be shipping this in Xcode 8 Beta already).

We might need to invest some work into optimising it for the debug build use case, though – at least from the blog post, it sounds like they are exclusively working on release builds so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment