Skip to content

Commit 28dc0f9

Browse files
justinhorvitzcopybara-github
authored andcommitted
Switch RemoteFileArtifactValue subclassing to optimize for memory cost.
Adding `long expireAtEpochMilli` added 8 bytes to every `RemoteFileArtifactValue`, even ones without an expiration time. In contrast, `PathFragment materializationExecPath` is just a pointer so only costs 4 bytes, and since without it the cost is rounded up to the next multiple of 8 anyway (28 -> 32), it is free to add this field. Conveniently, since the expiration doesn't factor into `hashCode`/`equals`, it's actually simpler to arrange the classes this way. PiperOrigin-RevId: 514388630 Change-Id: I50a7e4e03b266f1a2dfad2fa38005c0c9a0cfa10
1 parent 7c235ff commit 28dc0f9

File tree

1 file changed

+59
-77
lines changed

1 file changed

+59
-77
lines changed

src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java

Lines changed: 59 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ public long getSize() {
379379
}
380380

381381
@Override
382-
public boolean wasModifiedSinceDigest(Path path) throws IOException {
382+
public boolean wasModifiedSinceDigest(Path path) {
383383
return false;
384384
}
385385

@@ -540,24 +540,26 @@ protected boolean couldBeModifiedByMetadata(FileArtifactValue o) {
540540

541541
/** Metadata for remotely stored files. */
542542
public static class RemoteFileArtifactValue extends FileArtifactValue {
543-
protected final byte[] digest;
544-
protected final long size;
545-
protected final int locationIndex;
546-
// The time when the remote file expires in milliseconds since epoch. negative value means the
547-
// remote file will never expire. This field doesn't contribute to the equality of the metadata.
548-
protected final long expireAtEpochMilli;
543+
private final byte[] digest;
544+
private final long size;
545+
private final int locationIndex;
546+
@Nullable private final PathFragment materializationExecPath;
549547

550548
private RemoteFileArtifactValue(
551-
byte[] digest, long size, int locationIndex, long expireAtEpochMilli) {
549+
byte[] digest,
550+
long size,
551+
int locationIndex,
552+
@Nullable PathFragment materializationExecPath) {
552553
this.digest = Preconditions.checkNotNull(digest);
553554
this.size = size;
554555
this.locationIndex = locationIndex;
555-
this.expireAtEpochMilli = expireAtEpochMilli;
556+
this.materializationExecPath = materializationExecPath;
556557
}
557558

558559
public static RemoteFileArtifactValue create(
559560
byte[] digest, long size, int locationIndex, long expireAtEpochMilli) {
560-
return new RemoteFileArtifactValue(digest, size, locationIndex, expireAtEpochMilli);
561+
return create(
562+
digest, size, locationIndex, expireAtEpochMilli, /* materializationExecPath= */ null);
561563
}
562564

563565
public static RemoteFileArtifactValue create(
@@ -566,147 +568,127 @@ public static RemoteFileArtifactValue create(
566568
int locationIndex,
567569
long expireAtEpochMilli,
568570
@Nullable PathFragment materializationExecPath) {
569-
if (materializationExecPath != null) {
570-
return new RemoteFileArtifactValueWithMaterializationPath(
571-
digest, size, locationIndex, expireAtEpochMilli, materializationExecPath);
572-
}
573-
return new RemoteFileArtifactValue(digest, size, locationIndex, expireAtEpochMilli);
571+
return expireAtEpochMilli < 0
572+
? new RemoteFileArtifactValue(digest, size, locationIndex, materializationExecPath)
573+
: new RemoteFileArtifactValueWithExpiration(
574+
digest, size, locationIndex, materializationExecPath, expireAtEpochMilli);
574575
}
575576

576577
@Override
577578
public boolean equals(Object o) {
579+
if (this == o) {
580+
return true;
581+
}
578582
if (!(o instanceof RemoteFileArtifactValue)) {
579583
return false;
580584
}
581585

582586
RemoteFileArtifactValue that = (RemoteFileArtifactValue) o;
583587
return Arrays.equals(digest, that.digest)
584588
&& size == that.size
585-
&& locationIndex == that.locationIndex;
589+
&& locationIndex == that.locationIndex
590+
&& Objects.equals(materializationExecPath, that.materializationExecPath);
586591
}
587592

588593
@Override
589-
public int hashCode() {
590-
return Objects.hash(Arrays.hashCode(digest), size, locationIndex);
594+
public final int hashCode() {
595+
return Objects.hash(Arrays.hashCode(digest), size, locationIndex, materializationExecPath);
591596
}
592597

593598
@Override
594-
public FileStateType getType() {
599+
public final FileStateType getType() {
595600
return FileStateType.REGULAR_FILE;
596601
}
597602

598603
@Override
599-
public byte[] getDigest() {
604+
public final byte[] getDigest() {
600605
return digest;
601606
}
602607

603608
@Override
604-
public FileContentsProxy getContentsProxy() {
609+
public final FileContentsProxy getContentsProxy() {
605610
throw new UnsupportedOperationException();
606611
}
607612

608613
@Override
609-
public long getSize() {
614+
public final long getSize() {
610615
return size;
611616
}
612617

613618
@Override
614-
public long getModifiedTime() {
619+
public final long getModifiedTime() {
615620
throw new UnsupportedOperationException(
616621
"RemoteFileArtifactValue doesn't support getModifiedTime");
617622
}
618623

619624
@Override
620-
public int getLocationIndex() {
625+
public final int getLocationIndex() {
621626
return locationIndex;
622627
}
623628

629+
@Override
630+
public final Optional<PathFragment> getMaterializationExecPath() {
631+
return Optional.ofNullable(materializationExecPath);
632+
}
633+
634+
/**
635+
* Returns the time when the remote file expires in milliseconds since epoch. A negative value
636+
* means the remote is not known to expire.
637+
*
638+
* <p>Expiration time does not contribute to equality of remote files.
639+
*/
624640
public long getExpireAtEpochMilli() {
625-
return expireAtEpochMilli;
641+
return -1;
626642
}
627643

628644
public boolean isAlive(Instant now) {
629-
if (expireAtEpochMilli < 0) {
630-
return true;
631-
}
632-
633-
return now.toEpochMilli() < expireAtEpochMilli;
645+
return true;
634646
}
635647

636648
@Override
637-
public boolean wasModifiedSinceDigest(Path path) {
649+
public final boolean wasModifiedSinceDigest(Path path) {
638650
return false;
639651
}
640652

641653
@Override
642-
public boolean isRemote() {
654+
public final boolean isRemote() {
643655
return true;
644656
}
645657

646658
@Override
647-
public String toString() {
659+
public final String toString() {
648660
return MoreObjects.toStringHelper(this)
649661
.add("digest", bytesToString(digest))
650662
.add("size", size)
651663
.add("locationIndex", locationIndex)
652-
.add("expireAtEpochMilli", expireAtEpochMilli)
664+
.add("materializationExecPath", materializationExecPath)
665+
.add("expireAtEpochMilli", getExpireAtEpochMilli())
653666
.toString();
654667
}
655668
}
656669

657-
/**
658-
* A remote artifact that should be materialized in the local filesystem as a symlink to another
659-
* location.
660-
*
661-
* <p>See the documentation for {@link FileArtifactValue#getMaterializationExecPath}.
662-
*/
663-
public static final class RemoteFileArtifactValueWithMaterializationPath
664-
extends RemoteFileArtifactValue {
665-
private final PathFragment materializationExecPath;
670+
/** A remote artifact that expires at a particular time. */
671+
private static final class RemoteFileArtifactValueWithExpiration extends RemoteFileArtifactValue {
672+
private final long expireAtEpochMilli;
666673

667-
private RemoteFileArtifactValueWithMaterializationPath(
674+
private RemoteFileArtifactValueWithExpiration(
668675
byte[] digest,
669676
long size,
670677
int locationIndex,
671-
long expireAtEpochMilli,
672-
PathFragment materializationExecPath) {
673-
super(digest, size, locationIndex, expireAtEpochMilli);
674-
this.materializationExecPath = Preconditions.checkNotNull(materializationExecPath);
675-
}
676-
677-
@Override
678-
public Optional<PathFragment> getMaterializationExecPath() {
679-
return Optional.ofNullable(materializationExecPath);
680-
}
681-
682-
@Override
683-
public boolean equals(Object o) {
684-
if (!(o instanceof RemoteFileArtifactValueWithMaterializationPath)) {
685-
return false;
686-
}
687-
688-
RemoteFileArtifactValueWithMaterializationPath that =
689-
(RemoteFileArtifactValueWithMaterializationPath) o;
690-
return Arrays.equals(digest, that.digest)
691-
&& size == that.size
692-
&& locationIndex == that.locationIndex
693-
&& Objects.equals(materializationExecPath, that.materializationExecPath);
678+
PathFragment materializationExecPath,
679+
long expireAtEpochMilli) {
680+
super(digest, size, locationIndex, materializationExecPath);
681+
this.expireAtEpochMilli = expireAtEpochMilli;
694682
}
695683

696684
@Override
697-
public int hashCode() {
698-
return Objects.hash(Arrays.hashCode(digest), size, locationIndex, materializationExecPath);
685+
public long getExpireAtEpochMilli() {
686+
return expireAtEpochMilli;
699687
}
700688

701689
@Override
702-
public String toString() {
703-
return MoreObjects.toStringHelper(this)
704-
.add("digest", bytesToString(digest))
705-
.add("size", size)
706-
.add("locationIndex", locationIndex)
707-
.add("expireAtEpochMilli", expireAtEpochMilli)
708-
.add("materializationExecPath", materializationExecPath)
709-
.toString();
690+
public boolean isAlive(Instant now) {
691+
return now.toEpochMilli() < expireAtEpochMilli;
710692
}
711693
}
712694

0 commit comments

Comments
 (0)