-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A large amount of static analysis time is spent computing hashcodes for maps. A significant performance improvement can be made by creating specialised classes for the common cases of contexts with 0, 1, or 2 values.
- Loading branch information
Henry Coles
committed
May 18, 2022
1 parent
fbc3215
commit b209fc0
Showing
10 changed files
with
348 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 14 additions & 41 deletions
55
pitest-entry/src/main/java/org/pitest/sequence/Context.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,32 @@ | ||
package org.pitest.sequence; | ||
|
||
import java.util.IdentityHashMap; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
public final class Context { | ||
|
||
private final boolean debug; | ||
private final Map<Slot,Object> slots; | ||
|
||
Context(Map<Slot,Object> slots, boolean debug) { | ||
this.slots = slots; | ||
this.debug = debug; | ||
} | ||
public interface Context { | ||
|
||
public static Context start() { | ||
static Context start() { | ||
return start(false); | ||
} | ||
|
||
public static Context start(boolean debug) { | ||
return new Context(new IdentityHashMap<>(), debug); | ||
static Context start(boolean debug) { | ||
if (debug) { | ||
return EmptyContext.WITH_DEBUG; | ||
} | ||
return EmptyContext.WITHOUT_DEBUG; | ||
} | ||
|
||
public <S> Context store(SlotWrite<S> slot, S value) { | ||
Map<Slot,Object> mutatedSlots = new IdentityHashMap<>(slots); | ||
mutatedSlots.put(slot.slot(), value); | ||
return new Context(mutatedSlots, debug); | ||
} | ||
<S> Context store(SlotWrite<S> slot, S value); | ||
|
||
@SuppressWarnings("unchecked") | ||
public <S> Optional<S> retrieve(SlotRead<S> slot) { | ||
return Optional.ofNullable((S)slots.get(slot.slot())); | ||
} | ||
<S> Optional<S> retrieve(SlotRead<S> slot); | ||
|
||
public <T> void debug(String msg, T t) { | ||
if (this.debug) { | ||
System.out.println(msg + " for " + t); | ||
} | ||
default boolean debug() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof Context)) { | ||
return false; | ||
default <T> void debug(String msg, T t) { | ||
if (debug()) { | ||
System.out.println(msg + " for " + t); | ||
} | ||
Context context = (Context) o; | ||
return slots.equals(context.slots); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return slots.hashCode(); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
pitest-entry/src/main/java/org/pitest/sequence/Context1.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package org.pitest.sequence; | ||
|
||
import java.util.IdentityHashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Specialisation of context for single values | ||
*/ | ||
final class Context1 implements Context { | ||
private final boolean debug; | ||
private final Slot<?> slot; | ||
private final Object value; | ||
|
||
Context1(Slot<?> slot, Object value, boolean debug) { | ||
this.slot = slot; | ||
this.value = value; | ||
this.debug = debug; | ||
} | ||
|
||
@Override | ||
public boolean debug() { | ||
return debug; | ||
} | ||
|
||
@Override | ||
public <S> Context store(SlotWrite<S> slot, S value) { | ||
Map<Slot,Object> mutatedSlots = new IdentityHashMap<>(); | ||
mutatedSlots.put(this.slot, this.value); | ||
mutatedSlots.put(slot.slot(), value); | ||
return new Context2(this.slot, this.value, slot.slot(), value, debug); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <S> Optional<S> retrieve(SlotRead<S> read) { | ||
if (read.slot().equals(slot)) { | ||
return (Optional<S>) Optional.ofNullable(value); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof Context1)) { | ||
return false; | ||
} | ||
Context1 context1 = (Context1) o; | ||
return slot.equals(context1.slot) && Objects.equals(value, context1.value); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(slot, value); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
pitest-entry/src/main/java/org/pitest/sequence/Context2.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package org.pitest.sequence; | ||
|
||
import java.util.IdentityHashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Specialisation of context for two values | ||
*/ | ||
final class Context2 implements Context { | ||
private final boolean debug; | ||
private final Slot<?> slot; | ||
private final Object value; | ||
private final Slot<?> slot2; | ||
private final Object value2; | ||
|
||
Context2(Slot<?> slot, Object value, Slot<?> slot2, Object value2, boolean debug) { | ||
this.slot = slot; | ||
this.value = value; | ||
this.slot2 = slot2; | ||
this.value2 = value2; | ||
this.debug = debug; | ||
} | ||
|
||
@Override | ||
public boolean debug() { | ||
return debug; | ||
} | ||
|
||
@Override | ||
public <S> Context store(SlotWrite<S> slot, S value) { | ||
Map<Slot,Object> mutatedSlots = new IdentityHashMap<>(); | ||
mutatedSlots.put(this.slot, this.value); | ||
mutatedSlots.put(this.slot2, this.value2); | ||
mutatedSlots.put(slot.slot(), value); | ||
return new MultiContext(mutatedSlots, debug); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <S> Optional<S> retrieve(SlotRead<S> read) { | ||
if (read.slot().equals(slot)) { | ||
return (Optional<S>) Optional.ofNullable(value); | ||
} | ||
|
||
if (read.slot().equals(slot2)) { | ||
return (Optional<S>) Optional.ofNullable(value2); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof Context2)) { | ||
return false; | ||
} | ||
Context2 context2 = (Context2) o; | ||
return slot.equals(context2.slot) && Objects.equals(value, context2.value) | ||
&& slot2.equals(context2.slot2) && Objects.equals(value2, context2.value2); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(slot, value, slot2, value2); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
pitest-entry/src/main/java/org/pitest/sequence/EmptyContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.pitest.sequence; | ||
|
||
import java.util.Optional; | ||
|
||
/** | ||
* Specialisation of context with no data | ||
*/ | ||
enum EmptyContext implements Context { | ||
|
||
WITHOUT_DEBUG(false), | ||
WITH_DEBUG(true); | ||
|
||
private final boolean debug; | ||
|
||
EmptyContext(boolean debug) { | ||
this.debug = debug; | ||
} | ||
|
||
@Override | ||
public boolean debug() { | ||
return debug; | ||
} | ||
|
||
@Override | ||
public <S> Context store(SlotWrite<S> slot, S value) { | ||
return new Context1(slot.slot(), value, debug); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <S> Optional<S> retrieve(SlotRead<S> slot) { | ||
return Optional.empty(); | ||
} | ||
|
||
} |
55 changes: 55 additions & 0 deletions
55
pitest-entry/src/main/java/org/pitest/sequence/MultiContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package org.pitest.sequence; | ||
|
||
import java.util.IdentityHashMap; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Specialisation of context for unlimited values | ||
*/ | ||
final class MultiContext implements Context { | ||
|
||
private final boolean debug; | ||
private final Map<Slot,Object> slots; | ||
|
||
MultiContext(Map<Slot,Object> slots, boolean debug) { | ||
this.slots = slots; | ||
this.debug = debug; | ||
} | ||
|
||
@Override | ||
public boolean debug() { | ||
return debug; | ||
} | ||
|
||
@Override | ||
public <S> Context store(SlotWrite<S> slot, S value) { | ||
Map<Slot,Object> mutatedSlots = new IdentityHashMap<>(slots); | ||
mutatedSlots.put(slot.slot(), value); | ||
return new MultiContext(mutatedSlots, debug); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <S> Optional<S> retrieve(SlotRead<S> slot) { | ||
return Optional.ofNullable((S)slots.get(slot.slot())); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof MultiContext)) { | ||
return false; | ||
} | ||
MultiContext context = (MultiContext) o; | ||
return slots.equals(context.slots); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return slots.hashCode(); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
pitest-entry/src/test/java/org/pitest/sequence/Context1Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.pitest.sequence; | ||
|
||
import nl.jqno.equalsverifier.EqualsVerifier; | ||
import org.junit.Test; | ||
|
||
public class Context1Test { | ||
|
||
@Test | ||
public void obeysHashCodeEqualsContract() { | ||
EqualsVerifier.forClass(Context1.class) | ||
.withNonnullFields("slot") | ||
.withIgnoredFields("debug") | ||
.verify(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
pitest-entry/src/test/java/org/pitest/sequence/Context2Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.pitest.sequence; | ||
|
||
import nl.jqno.equalsverifier.EqualsVerifier; | ||
import org.junit.Test; | ||
|
||
public class Context2Test { | ||
|
||
@Test | ||
public void obeysHashCodeEqualsContract() { | ||
EqualsVerifier.forClass(Context2.class) | ||
.withNonnullFields("slot") | ||
.withNonnullFields("slot2") | ||
.withIgnoredFields("debug") | ||
.verify(); | ||
} | ||
} |
Oops, something went wrong.