Offline Assembler build step always computes hashes even when nothing changes

# Darin Adler (a day ago)

I noticed that the “Offline Assembler” build step was taking between 5 and 30 seconds every time I build. Really stands out in incremental builds. I realized that this step does not do any dependency analysis. Every time, it builds a hash of the input to see if it needs to recompute the assembly.

That’s probably not the best pattern; normally we like to use file modification dates to avoid doing any work when files haven’t changed.

Is there someone who can help me fix this so we get faster incremental builds?

— Darin

Contact us to advertise here
# Michael Saboff (a day ago)

Darin,

I can take a look at this.

Filed <https://bugs.webkit.org/show_bug.cgi?id=189654 <https://bugs.webkit.org/show_bug.cgi?id=189654>> “Offline assembler always computes hashes even when nothing changes"

# Filip Pizlo (a day ago)

On Sep 16, 2018, at 4:03 PM, Darin Adler <darin at apple.com> wrote:

I noticed that the “Offline Assembler” build step was taking between 5 and 30 seconds every time I build. Really stands out in incremental builds. I realized that this step does not do any dependency analysis. Every time, it builds a hash of the input to see if it needs to recompute the assembly.

Yup, that’s quite intentional.

That’s probably not the best pattern; normally we like to use file modification dates to avoid doing any work when files haven’t changed.

I don’t totally remember the details, but it’s not that simple. I vaguely recall a previous attempt to fix this that had to be reverted because it resulted in too many broken builds.

Which offline assembler build step are you referring to? There is more than one. I think it’s the step that generates the LLInt using the offset extractor binary as input.

Note that one problem is that this step is slower than it could be. We sometimes regress it a lot and then make it faster again. Maybe we regressed it recently.

Is there someone who can help me fix this so we get faster incremental builds?

Sounds like Michael volunteered.

I would favor a sure-to-be-sound fix of just making that phase run faster, possibly by reducing the number of options that the llint uses. That might get you the outcome you want (faster builds) without the risk of bad builds.

# Darin Adler (a day ago)

On Sep 16, 2018, at 5:59 PM, Filip Pizlo <fpizlo at apple.com> wrote:

Which offline assembler build step are you referring to?

The one that is the “Offline Assembler” target in Xcode, which runs this command:

ruby JavaScriptCore/offlineasm/asm.rb JavaScriptCore/llint/LowLevelInterpreter.asm "${BUILT_PRODUCTS_DIR}/JSCLLIntOffsetsExtractor” LLIntAssembly.h

For a “nothing rebuild” of all of WebKit and all of Safari for iOS on my iMac, it takes about 10 seconds out of a 30 second total “build" time.

Looking more carefully at the build log now, it seems that recompiling LLIntOffsetExtractor.cpp is also taking multiple seconds. Not executing generate_offset_extractor.rb, but compiling the output.

— Darin

# Filip Pizlo (12 hours ago)

On Sep 16, 2018, at 8:48 PM, Darin Adler <darin at apple.com> wrote:

On Sep 16, 2018, at 5:59 PM, Filip Pizlo <fpizlo at apple.com> wrote:

Which offline assembler build step are you referring to?

The one that is the “Offline Assembler” target in Xcode, which runs this command:

ruby JavaScriptCore/offlineasm/asm.rb JavaScriptCore/llint/LowLevelInterpreter.asm "${BUILT_PRODUCTS_DIR}/JSCLLIntOffsetsExtractor” LLIntAssembly.h

For a “nothing rebuild” of all of WebKit and all of Safari for iOS on my iMac, it takes about 10 seconds out of a 30 second total “build" time.

Looking more carefully at the build log now, it seems that recompiling LLIntOffsetExtractor.cpp is also taking multiple seconds. Not executing generate_offset_extractor.rb, but compiling the output.

Does every build that you do rebuild LLIntOffsetExtractor.cpp? Including a clean build?

# Darin Adler (10 hours ago)

I don’t know.

Sent from my iPhone

# Filip Pizlo (9 hours ago)

Sorry, I should have asked: does it even rebuild when you change nothing?

That llint step really does depend on most headers in WTF and JSC, so if you change any of them then I would expect a rebuild of that file. It may be that the right solution is to make that step faster and to make it possible to run it in parallel to other steps.

# Keith Miller (9 hours ago)

IIRC, it spends a bunch of time in the offline assembler ruby scripts. AFAIK, those scripts only depend on the LowLevelInterpreter*.asm files. So we should only need to run it if those files have changed. Or, for the assembly generation half, if there is also a new object offsets binary (from LLIntOffsetsExtractor.cpp).

# Filip Pizlo (7 hours ago)

On Sep 17, 2018, at 10:36 AM, Keith Miller <keith_miller at apple.com> wrote:

IIRC, it spends a bunch of time in the offline assembler ruby scripts. AFAIK, those scripts only depend on the LowLevelInterpreter*.asm files.

We’re talking about the offset extractor binary, which depends on most of JSC’s and WTF’s headers, and the asm.rb build, which depends on:

  • all of the offlineasm ruby scripts
  • all of the *.asm files
  • everything that the offset extractor binary depended on (so most of JSC’s and WTF’s headers).

So we should only need to run it if those files have changed. Or, for the assembly generation half, if there is also a new object offsets binary (from LLIntOffsetsExtractor.cpp).

We’re talking about the compiling of the offset extractor binary and the assembly generation half.

# Darin Adler (6 hours ago)

Sent from my iPhone

On Sep 17, 2018, at 10:21 AM, Filip Pizlo <fpizlo at apple.com> wrote:

Sorry, I should have asked: does it even rebuild when you change nothing?

I don’t know what it’s doing, only how long it takes. My test was to build and then build again. In many cases I am only changing a source file far downstream in dependencies but I am rebuilding everything because I just push the build button in Xcode or type make.

That llint step really does depend on most headers in WTF and JSC, so if you change any of them then I would expect a rebuild of that file.

I was not changing headers.

It may be that the right solution is to make that step faster and to make it possible to run it in parallel to other steps.

In many other cases skipping the entire build step based on file modification times is a great approach. I understand it might be hard to know which files need to be checked in a case like this so maybe that’s why we can’t apply the classic solution.

— Darin

# Filip Pizlo (5 hours ago)

On Sep 17, 2018, at 1:41 PM, Darin Adler <darin at apple.com> wrote:

Sent from my iPhone

On Sep 17, 2018, at 10:21 AM, Filip Pizlo <fpizlo at apple.com> wrote:

Sorry, I should have asked: does it even rebuild when you change nothing?

I don’t know what it’s doing, only how long it takes. My test was to build and then build again. In many cases I am only changing a source file far downstream in dependencies but I am rebuilding everything because I just push the build button in Xcode or type make.

That llint step really does depend on most headers in WTF and JSC, so if you change any of them then I would expect a rebuild of that file.

I was not changing headers.

It may be that the right solution is to make that step faster and to make it possible to run it in parallel to other steps.

In many other cases skipping the entire build step based on file modification times is a great approach. I understand it might be hard to know which files need to be checked in a case like this so maybe that’s why we can’t apply the classic solution.

Also, there are many changes to the headers that don’t necessitate a llint rebuild. The use of hashes is supposed to protect against this.

After looking at it with Michael, we believe that the issue is just how the hash is computed. There’s a step where Ruby code is building a ruby array of ruby ints to represent a file and then looping. That should probably use strings and regexes.

Want more features?

Request early access to our private beta of readable email premium.