-
Notifications
You must be signed in to change notification settings - Fork 388
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CLDR-17327 speedup VoteResolver's transcript
- skip String.format when unneeded - defer all transcript operations until needed measured as between a 5 and 10x speedup
- Loading branch information
Showing
4 changed files
with
178 additions
and
12 deletions.
There are no files selected for viewing
72 changes: 72 additions & 0 deletions
72
tools/cldr-code/src/main/java/org/unicode/cldr/util/DeferredTranscript.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,72 @@ | ||
package org.unicode.cldr.util; | ||
|
||
import com.google.common.base.Supplier; | ||
import com.google.common.base.Suppliers; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* lazily-calculates the transcript. Thread safe to call get() multiple times once commit() is | ||
* called and before clear() is called. | ||
*/ | ||
public class DeferredTranscript implements Supplier<String> { | ||
|
||
private final class FormatEntry { | ||
public final CharSequence fmt; | ||
public final Object[] args; | ||
|
||
public FormatEntry(String fmt, Object[] args) { | ||
this.fmt = fmt; | ||
this.args = args; | ||
} | ||
|
||
public final CharSequence format() { | ||
if (args == null || args.length == 0) { | ||
return fmt; | ||
} else { | ||
return String.format(fmt.toString(), args); | ||
} | ||
} | ||
} | ||
|
||
public DeferredTranscript() { | ||
clear(); | ||
} | ||
|
||
private Supplier<String> delegate = null; | ||
private List<FormatEntry> formats; | ||
|
||
/** reset this transcript so it can be used again. */ | ||
public void clear() { | ||
// the delegate only calculates the value once, the first time it is accessed. | ||
delegate = | ||
Suppliers.memoize( | ||
new Supplier<String>() { | ||
|
||
@Override | ||
public String get() { | ||
return formats.stream() | ||
.map(FormatEntry::format) | ||
.collect(Collectors.joining("\n")); | ||
} | ||
}); | ||
formats = new LinkedList<>(); | ||
} | ||
|
||
@Override | ||
public final String get() { | ||
return delegate.get(); | ||
} | ||
|
||
/** | ||
* Add a formatted entry. Will be ignored if get() has been called since clear() | ||
* | ||
* @param fmt the string or pattern string | ||
* @param args if non-null, arguments to String.format() | ||
*/ | ||
final DeferredTranscript add(String fmt, Object... args) { | ||
formats.add(new FormatEntry(fmt, args)); | ||
return this; | ||
} | ||
} |
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
60 changes: 60 additions & 0 deletions
60
tools/cldr-code/src/test/java/org/unicode/cldr/util/TestDeferredTranscript.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.unicode.cldr.util; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
public class TestDeferredTranscript { | ||
@Test | ||
public void TestBasic() { | ||
DeferredTranscript dt = new DeferredTranscript(); | ||
|
||
dt.clear(); | ||
|
||
assertEquals("", dt.get()); | ||
assertEquals("", dt.get()); | ||
dt.clear(); | ||
dt.add("Hello"); | ||
assertEquals("Hello", dt.get()); | ||
// all of these are ignored, we've already committed the output. | ||
dt.add("Hello"); | ||
dt.add("Hello"); | ||
dt.add("Hello"); | ||
dt.add("Goodbye %d times", 999); | ||
assertEquals("Hello", dt.get()); | ||
dt.clear(); | ||
dt.add("Hello"); | ||
dt.add("Hello"); | ||
assertEquals("Hello\nHello", dt.get()); | ||
assertEquals("Hello\nHello", dt.get()); | ||
dt.clear(); | ||
assertEquals("", dt.get()); | ||
dt.clear(); | ||
dt.add("You have %d problems.", 0); | ||
dt.add("Thanks"); | ||
assertEquals("You have 0 problems.\nThanks", dt.get()); | ||
dt.add("Thanks"); | ||
assertEquals("You have 0 problems.\nThanks", dt.get()); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(booleans = {false, true}) | ||
public void TestPerf(boolean doGet) { | ||
DeferredTranscript dt = new DeferredTranscript(); | ||
for (int i = 0; i < 100000; i++) { | ||
dt.add("Now you have %d problem(s).", i); | ||
} | ||
// 1M iterations: 2.3 seconds without get(), 10 seconds with | ||
if (doGet) { | ||
// if doGet is false, the above code is faster. | ||
// defer the formatting | ||
final String s0 = dt.get(); | ||
assertNotNull(s0); | ||
final String s1 = dt.get(); | ||
assertEquals(s0, s1); | ||
} | ||
} | ||
} |
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