Skip to content

Commit

Permalink
Query performance: compute hashCode just-in-time
Browse files Browse the repository at this point in the history
For queries that output a lot of data to stdout, computing Event hashes
turns out to be on the critical path as it's done eagerly on packets
that are 4 KB or larger, but then never used.

This does not use locking, so multiple threads attempting to get the
hash code at the same time can race, and also see h != 0 on the first
read, and then h == 0 on the second. Therefore, we need to use this
construction, which makes the race benign.

PiperOrigin-RevId: 257472502
  • Loading branch information
ulfjack authored and copybara-github committed Jul 10, 2019
1 parent 0c2eb90 commit e9521d3
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions src/main/java/com/google/devtools/build/lib/events/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public final class Event implements Serializable {
@Nullable
private final String tag;

private final int hashCode;
private int hashCode;

private Event(
EventKind kind,
Expand All @@ -62,8 +62,6 @@ private Event(
this.tag = tag;
this.stdout = stdout;
this.stderr = stderr;
this.hashCode =
Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes), stdout, stderr);
}

private Event(
Expand All @@ -80,8 +78,6 @@ private Event(
this.tag = tag;
this.stdout = stdout;
this.stderr = stderr;
this.hashCode =
Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes), stdout, stderr);
}

public Event withTag(String tag) {
Expand Down Expand Up @@ -162,7 +158,21 @@ public String toString() {

@Override
public int hashCode() {
return hashCode;
// We defer the computation of hashCode until it is needed to avoid the overhead of computing it
// and then never using it. In particular, we use Event for streaming stdout and stderr, which
// are both large and the hashCode is never used.
//
// This uses the same construction as String.hashCode. We don't lock, so reads and writes to the
// field can race. However, integer reads and writes are atomic, and this code guarantees that
// all writes have the same value, so the memory location can only be either 0 or the final
// value. Note that a reader could see the final value on the first read and 0 on the second
// read, so we must take care to only read the field once.
int h = hashCode;
if (h == 0) {
h = Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes), stdout, stderr);
hashCode = h;
}
return h;
}

@Override
Expand Down

0 comments on commit e9521d3

Please sign in to comment.