-
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 or 1 values.
- Loading branch information
Henry Coles
committed
May 18, 2022
1 parent
fbc3215
commit dfc8d5d
Showing
8 changed files
with
262 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 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); | ||
} | ||
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); | ||
} | ||
} |
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(); | ||
} | ||
} |
72 changes: 66 additions & 6 deletions
72
pitest-entry/src/test/java/org/pitest/sequence/ContextTest.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,16 +1,76 @@ | ||
package org.pitest.sequence; | ||
|
||
import nl.jqno.equalsverifier.EqualsVerifier; | ||
import org.junit.Test; | ||
|
||
import java.util.Optional; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class ContextTest { | ||
|
||
@Test | ||
public void obeysHashCodeEqualsContract() { | ||
EqualsVerifier.forClass(Context.class) | ||
.withNonnullFields("slots") | ||
.withIgnoredFields("debug") | ||
.verify(); | ||
public void retrieveIsStartsEmpty() { | ||
SlotRead<Integer> slot = Slot.create(Integer.class).read(); | ||
assertThat(Context.start().retrieve(slot)).isEmpty(); | ||
} | ||
|
||
@Test | ||
public void canStoreThenRetrieve() { | ||
SlotRead<Integer> slot = Slot.create(Integer.class).read(); | ||
Context underTest = Context.start().store(slot.slot().write(), 42); | ||
|
||
Optional<Integer> actual = underTest.retrieve(slot); | ||
assertThat(actual).contains(42); | ||
} | ||
|
||
@Test | ||
public void canStoreAndRetrieveTwoValues() { | ||
Slot<Integer> slot1 = Slot.create(Integer.class); | ||
Slot<Integer> slot2 = Slot.create(Integer.class); | ||
Context underTest = Context.start() | ||
.store(slot1.write(), 42) | ||
.store(slot2.write(), 101); | ||
|
||
assertThat(underTest.retrieve(slot1.read())).contains(42); | ||
assertThat(underTest.retrieve(slot2.read())).contains(101); | ||
} | ||
|
||
@Test | ||
public void canStoreAndRetrieveThreeValues() { | ||
Slot<Integer> slot1 = Slot.create(Integer.class); | ||
Slot<Integer> slot2 = Slot.create(Integer.class); | ||
Slot<Integer> slot3 = Slot.create(Integer.class); | ||
Context underTest = Context.start() | ||
.store(slot1.write(), 42) | ||
.store(slot2.write(), 101) | ||
.store(slot3.write(), 8); | ||
|
||
assertThat(underTest.retrieve(slot1.read())).contains(42); | ||
assertThat(underTest.retrieve(slot2.read())).contains(101); | ||
assertThat(underTest.retrieve(slot3.read())).contains(8); | ||
} | ||
|
||
@Test | ||
public void canStoreAndRetrieveMultipleValues() { | ||
Slot<Integer> slot1 = Slot.create(Integer.class); | ||
Slot<Integer> slot2 = Slot.create(Integer.class); | ||
Slot<Integer> slot3 = Slot.create(Integer.class); | ||
Slot<Integer> slot4 = Slot.create(Integer.class); | ||
Slot<Integer> slot5 = Slot.create(Integer.class); | ||
Context underTest = Context.start() | ||
.store(slot1.write(), 1) | ||
.store(slot2.write(), 2) | ||
.store(slot3.write(), 3) | ||
.store(slot4.write(), 4) | ||
.store(slot5.write(), 5); | ||
|
||
|
||
assertThat(underTest.retrieve(slot1.read())).contains(1); | ||
assertThat(underTest.retrieve(slot2.read())).contains(2); | ||
assertThat(underTest.retrieve(slot3.read())).contains(3); | ||
assertThat(underTest.retrieve(slot4.read())).contains(4); | ||
assertThat(underTest.retrieve(slot5.read())).contains(5); | ||
} | ||
|
||
|
||
} |
16 changes: 16 additions & 0 deletions
16
pitest-entry/src/test/java/org/pitest/sequence/MultiContextTest.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 MultiContextTest { | ||
|
||
@Test | ||
public void obeysHashCodeEqualsContract() { | ||
EqualsVerifier.forClass(MultiContext.class) | ||
.withNonnullFields("slots") | ||
.withIgnoredFields("debug") | ||
.verify(); | ||
} | ||
|
||
} |