Skip to content
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 BLAKE3 hasher to vfs #18784

Closed
wants to merge 7 commits into from

Conversation

tylerwilliams
Copy link
Contributor

Add BLAKE3 hasher to vfs

This PR adds the Blake3Hasher and Blake3HashFunction classes to vfs and
makes them available under the flag --digest_function=BLAKE3.

I spent a little time trying to optimize performance more here, and am happy to
chat about different approaches (ByteBuffer vs byte[] vs unsafe vs mmap). In the
end this CL implements something pretty conservative, but there is room to get
more performance out of this with a different approach.

This is a partial commit for #18658.

@tylerwilliams
Copy link
Contributor Author

tylerwilliams commented Jun 27, 2023

@coeuvre -- Could use your advice on how to fix:

  1. //src/test/shell/bazel:bazel_bootstrap_distfile_test is failing, how is this supposed to interact with dist_http_archive? It seemed like that test should be able to run w/o network enabled -- how do I make it aware of the dist_http_archive sources?

  2. Clang on :ubuntu: 18.04 LTS (OpenJDK 11, gcc 7.5.0) -- If we can disable Ubuntu 18 (it's already EOLd), this should just go away. If not, I think possibly a repository_rule could work here to disable including AVX512 on the old ubuntu18 / gcc versions, but this is more complicated. This code is linked in but only used if the CPU supports it -- it's just that this old OS + compiler do not even support that flag afaik.

Thanks!

@tylerwilliams tylerwilliams marked this pull request as ready for review June 27, 2023 00:37
@github-actions github-actions bot added the awaiting-review PR is awaiting review from an assigned reviewer label Jun 27, 2023
@sgowroji sgowroji added the team-Remote-Exec Issues and PRs for the Execution (Remote) team label Jun 27, 2023
@coeuvre
Copy link
Member

coeuvre commented Jun 27, 2023

For 1, yes the test is to verify Bazel can be built without network. You need to add blake3 to distdir_deps.bzl.

@meteorcloudy what's your opinion on 2?

@coeuvre coeuvre self-requested a review June 27, 2023 08:51
@@ -82,8 +73,6 @@ private DigestHashFunction(
checkArgument(!names.isEmpty());
this.name = names.get(0);
this.names = names;
this.messageDigestPrototype = getMessageDigestInstance();
this.messageDigestPrototypeSupportsClone = supportsClone(messageDigestPrototype);
}

public static DigestHashFunction register(
Copy link
Member

Choose a reason for hiding this comment

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

We cannot change how the registration works here because we rely on this internally. You could, however, glue BLAKE3 into this with following steps:

  1. Write a class that implements java.security.MessageDigest for BLAKE3. This is the class that interact with JNI and call into c code.
  2. Register the MessageDigest from step 1 by implementing a java.security.Provider and add it with java.security.Security.addProvider(...). So that when you call MessageDigest.getInstance("BLAKE3"), you can get back the instance from the provider.
  3. Adapt DigestMessage for BLAKE3 into HashFunction by calling MessageDigestHashFunction.
  4. Create a DigestHashFunction by calling DigestHashFunction.register(...) using the HashFunction from step 3.

I know this is verbose and not fun but we have to write it in this way.

@meteorcloudy
Copy link
Member

For the bazel_bootstrap_distfile_test, the issue is subtle:

bazel/distdir_deps.bzl

Lines 276 to 281 in 75b0221

"blake3": {
"archive": "v1.3.3.zip",
"sha256": "bb529ba133c0256df49139bd403c17835edbf60d2ecd6463549c6a5fe279364d",
"urls": [
"https://github.com/BLAKE3-team/BLAKE3/archive/refs/tags/1.3.3.zip",
],

"archive": "v1.3.3.zip", should be "archive": "1.3.3.zip", to match the URL so that it actually got used during the bootstrap.

@meteorcloudy
Copy link
Member

For "Clang on :ubuntu: 18.04 LTS (OpenJDK 11, gcc 7.5.0)", we do plan to phase out ubuntu 1804 soon. But we need to schedule the work and ideally we do that for Bazel 7 not in the middle of a LTS release since we promises backwards compatibility. So if there is other ways to workaround it, that will be preferred.

@tylerwilliams tylerwilliams force-pushed the b3hasher2 branch 3 times, most recently from a904ba6 to 8e26aa5 Compare June 30, 2023 19:55
@tylerwilliams
Copy link
Contributor Author

tylerwilliams commented Jun 30, 2023

PTAL

@coeuvre -- I've made the changes you suggested and made the implementation a MessageDigest as well as reverting the changes to Fingerprint etc. Thank you for the detailed pointers there.

Re failing tests, I have merged bazelbuild/bazel-central-registry#698 to fix the bzlmod tests but now they are failing in a new way with:

(17:32:13) ERROR: Error computing the main repository mapping: Yanked version detected in your resolved dependency graph: rules_go@0.34.0, for the reason: Obsolete experimental version that emits debug prints. Update to 0.38.1 or higher..

Any ideas on how to fix this?

public final class Blake3Provider extends Provider {
public Blake3Provider() {
super("BLAKE3Provider", "1.0", "A BLAKE3 digest provider");
put("MessageDigest.BLAKE3", "com.google.devtools.build.lib.vfs.Blake3MessageDigest");
Copy link
Member

Choose a reason for hiding this comment

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

nit: Use Blake3MessageDigest.class.getName() to make refactor easier in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -67,6 +68,7 @@ public int getDigestMaximumLength() {

public static final DigestHashFunction SHA1 = register(Hashing.sha1(), "SHA-1", "SHA1");
public static final DigestHashFunction SHA256 = register(Hashing.sha256(), "SHA-256", "SHA256");
public static final DigestHashFunction BLAKE3 = register(new Blake3HashFunction(), "BLAKE3");
Copy link
Member

Choose a reason for hiding this comment

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

I think you can just use new MessageDigestHashFunction("BLAKE3") (doc) instead of implementing HashFunction by yourself in Blake3HashFunction.

Copy link
Member

Choose a reason for hiding this comment

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

As mentioned above, we don't want put BLAKE3 code in the common code path. Neither the registration here nor the provider below.

I suggest we have a utility class BazelHashFunctions which installs the provider and register the hash function, e.g.:

class BazelHashFunctions {
    static {
        addProvider(...);
    }

    public static final DigestHashFunction BLAKE3 = DigestHashFunction.register(...);
}

In a following PR when we want to create a file system based on BLAKE3, we could do that in BazelFileSystemModule with code like:

new UnixFileSystem(BazelHashFunctions.BLAKE3, ...);

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'm trying to read between the lines here and figure out where you want stuff / how you want it to be injected -- let me know what I got wrong.

I moved blake3 stuff into a bazel subdir of vfs. The provider is statically registered when BazelHashFunctions is loaded by the classloader in src/main/java/com/google/devtools/build/lib/bazel/Bazel.java. Re the MessageDigestHashFunction -- addressed above, but that's not public afaict.

Copy link
Member

@coeuvre coeuvre Jul 6, 2023

Choose a reason for hiding this comment

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

Yes, we want the provider to be statically registered by the classloader when it is loading class BazelHashFunctions. But we don't want to have it implementing BlazeModule and put it in src/main/java/com/google/devtools/build/lib/bazel/Bazel.java.

What I proposed above is we could add the code

new UnixFileSystem(BazelHashFunctions.BLAKE3, ...);

to somewhere like here so that when we want to use BLAKE3, it registers it.

We don't need to do it in this CL as we haven't focused on integrating BLAKE3 into Bazel's VFS. Let's limit the scope of this CL to only adapt BLAKE3 as a DigestHashFunction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you give a little more context on what else is required to add the digest function to vfs? I'm a little unclear on that part and it will help me for the next cl. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

Well, I think I overcomplicated that. The only necessary part is we want to register BLAKE3 for Bazel only. We can just force loading BazelHashFunctions by adding following code block in BazelFilesystemModule

class BazelFilesystemmodule {
    static {
        BazelHashFunctions.ensureRegistered();
    }
}

where BazelHashFunctions.ensureRegistered is just an empty static function whose sole purpose is to let JVM load class BazelHashFunctions and run the static block 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.

Thanks, this is just what I needed. Done.

import java.security.DigestException;
import java.security.MessageDigest;

public final class Blake3MessageDigest extends MessageDigest implements Hasher {
Copy link
Member

Choose a reason for hiding this comment

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

If you use MessageDigestHashFunction as suggested below, you can remove implements Hasher.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MessageDigestHashFunction is not public, and copying it in is prohibitive because it brings like 5 other classes.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I missed that. Can we then create a separate class to implement Hasher? I found it is confusing to combine these two especially when we have update, engineUpdate and share the underlying buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@tylerwilliams tylerwilliams force-pushed the b3hasher2 branch 2 times, most recently from 07fbd47 to c92ed72 Compare July 6, 2023 08:26
}

public void engineUpdate(byte[] data, int offset, int length) {
while (length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

One optimization here is we could avoid copying if the size of data is larger than ONESHOT_THRESHOLD. But this is totally optional and we could leave that for a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was trying to keep this simple for now -- enough complexity already :) Agree on deferring this for a followup (where we measure perf).

}

public void engineUpdate(byte b) {
engineUpdate(new byte[] {b});
Copy link
Member

Choose a reason for hiding this comment

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

Optional: avoid creating a new object, put the byte into buffer directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


@CanIgnoreReturnValue
public Hasher putBytes(ByteBuffer b) {
buffer = b;
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to copy/use the argument b now instead of saving it for use later. I believe the contract of the interface is the caller is free to use the passed buffer after the function is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right you are.

@tylerwilliams tylerwilliams force-pushed the b3hasher2 branch 2 times, most recently from 339d81d to 3d6b67e Compare July 8, 2023 05:30
Copy link
Member

@coeuvre coeuvre left a comment

Choose a reason for hiding this comment

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

Thanks, this PR is pretty good now! Just a few more comments and we are ready to go!

cc_binary(
name = "libunix_jni.so",
srcs = [
"blake3_jni.cc",
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this line since blake3_jni is already in the deps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

java_test(
name = "BazelTests",
size = "small",
tags = ["no_windows"],
Copy link
Member

Choose a reason for hiding this comment

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

I see BALKE3 code can be compiled on Windows. Are there any blockers for running the tests on windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

"c/blake3_avx512_x86-64_unix.S",
# Disable to appease bazel-ci which uses ubuntu-18 (EOL) and GCC 7
# lacking the headers to compile AVX512.
# "c/blake3_avx512_x86-64_unix.S",
Copy link
Collaborator

Choose a reason for hiding this comment

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

@meteorcloudy FYI, this is an unfortunate consequence of the old versions running in CI.

Copy link
Contributor

Choose a reason for hiding this comment

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

Related: #18784 (comment)

return env->ReleaseByteArrayElements(array, addr, 0);
}

extern "C" JNIEXPORT long JNICALL
Copy link
Collaborator

Choose a reason for hiding this comment

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

This returns long while the corresponding Java method returns int. It should also the JNIEnv * and jobject parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

@Override
protected void finalize() throws Throwable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

That usage is fine as it comes after the loadJni call. The failing test bootstraps Bazel and may not honor BUILD files, maybe the new JNI sources need to be listed somewhere else?

@tylerwilliams tylerwilliams force-pushed the b3hasher2 branch 7 times, most recently from 81ded64 to 890327f Compare July 19, 2023 05:14
@tylerwilliams
Copy link
Contributor Author

@coeuvre I believe this is ready for another look / attempt at merging. Thanks

@coeuvre
Copy link
Member

coeuvre commented Jul 20, 2023

Thanks, I am importing now!

@coeuvre
Copy link
Member

coeuvre commented Jul 21, 2023

Everything went well. Currently waiting for internal review.

@tylerwilliams
Copy link
Contributor Author

Everything went well. Currently waiting for internal review.

@coeuvre sweet, thanks! Btw did you see my comment on the gerrit repo? https://bazel-review.googlesource.com/c/bazel/+/225070

Hoping we can make that last minute change before it goes in. Thanks again!

@coeuvre
Copy link
Member

coeuvre commented Jul 21, 2023

Sorry, I didn't see that because there wasn't any notification :(.

Np, I can make the change now.

@github-actions github-actions bot removed the awaiting-review PR is awaiting review from an assigned reviewer label Jul 24, 2023
@brentleyjones
Copy link
Contributor

Thanks @coeuvre!

@tylerwilliams
Copy link
Contributor Author

@coeuvre thank you again for all your help shepherding this through the process

brentleyjones pushed a commit to brentleyjones/bazel that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in bazelbuild#18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9
(cherry picked from commit 6d6bbdc)
brentleyjones pushed a commit to brentleyjones/bazel that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in bazelbuild#18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9

(cherry picked from commit 6d6bbdc)
brentleyjones pushed a commit that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in #18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9

(cherry picked from commit 6d6bbdc)
brentleyjones pushed a commit to brentleyjones/bazel that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in bazelbuild#18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9

(cherry picked from commit 6d6bbdc)
brentleyjones pushed a commit that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in #18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9

(cherry picked from commit 6d6bbdc)
keertk pushed a commit that referenced this pull request Jul 25, 2023
Currently, it is included in runtime library with glob which is shared between bazel and blaze. Moving it to bazel package to prevent from packaging it when building blaze.

An upcoming change (in #18784) will package BLAKE3 to this module and we don't want it in blaze.

PiperOrigin-RevId: 547516042
Change-Id: I673fa3d6824d56c85e85b02d13b4eb56571edda9

(cherry picked from commit 6d6bbdc)

Co-authored-by: Googler <chiwang@google.com>
@iancha1992
Copy link
Member

The changes in this PR have been included in Bazel 6.4.0 RC1. Please test out the release candidate and report any issues as soon as possible. If you're using Bazelisk, you can point to the latest RC by setting USE_BAZEL_VERSION=last_rc.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-Remote-Exec Issues and PRs for the Execution (Remote) team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants