【发布时间】:2015-12-01 03:19:08
【问题描述】:
这是Full Source 和a direct link to the data
这些测试的时间差异很大,但执行相同。我想了解为什么时间不同。
private static final int ITERATIONS = 100;
private static final DataFactory RANDOM_DF = DataFactoryImpl.defaultInstance();
@Test // 6s
public void testGetMaxLength() throws Exception {
for ( int i = 1; i < ITERATIONS; i++ ) {
testGetMaxLength( i );
}
}
private void testGetMaxLength( final int length ) {
for ( int i = 0; i < ITERATIONS; i++ ) {
String word = RANDOM_DF.word().getMaxLength( length );
assertThat( word, not( isEmptyOrNullString() ) );
assertThat( word.length(), allOf( greaterThanOrEqualTo( 1 ), lessThanOrEqualTo( length ) ) );
}
}
@Test // 301ms
public void testGetLength() throws Exception {
for ( int i = 1; i < ITERATIONS; i++ ) {
testGetLength( i );
}
}
private void testGetLength( final int length ) {
for ( int i = 0; i < ITERATIONS; i++ ) {
String word = RANDOM_DF.word().getLength( length );
assertThat( word, not( isEmptyOrNullString() ) );
assertThat( word.length(), equalTo( length ) );
这是DataFactoryUtil 最有可能包含导致巨大差异的代码的类。
final class DataFactoryUtil {
private DataFactoryUtil() {
}
static <T> Optional<T> valueFromMap(
final Map<Integer, List<T>> map,
final IntUnaryOperator randomSupplier,
final int minInclusive,
final int maxInclusive
) {
List<T> list = map.entrySet()
.parallelStream() // line 26
.filter( e -> e.getKey() >= minInclusive && e.getKey() <= maxInclusive )
.map( Map.Entry::getValue )
.flatMap( Collection::stream )
.collect( Collectors.toList() );
return valueFromList( list, randomSupplier );
}
static <T> Optional<T> valueFromList( final List<T> list, final IntUnaryOperator randomSupplier ) {
int random = randomSupplier.applyAsInt( list.size() );
return list.isEmpty() ? Optional.empty() : Optional.of( list.get( random ) );
}
static List<String> dict() {
try {
URL url = DataFactoryUtil.class.getClassLoader().getResource( "dictionary" );
assert url != null;
return Files.lines( Paths.get( url.toURI() ) ).collect( Collectors.toList() );
}
catch ( URISyntaxException | IOException e ) {
throw new IllegalStateException( e );
}
}
}
这里是不同的实现
@FunctionalInterface
public interface RandomStringFactory {
default String getMaxLength( final int maxInclusive ) {
return this.getRange( 1, maxInclusive );
}
String getRange( final int minInclusive, final int maxInclusive );
default String getLength( int length ) {
return this.getRange( length, length );
}
}
以及word的实际实现
DataFactoryImpl( final IntBinaryOperator randomSource, final List<String> wordSource ) {
this.random = randomSource;
this.wordSource = wordSource.stream().collect( Collectors.groupingBy( String::length ) );
}
public static DataFactory defaultInstance() {
return new DataFactoryImpl( RandomUtils::nextInt, dict() );
}
default RandomStringFactory word() {
return ( min, max ) -> valueFromMap( getWordSource(), ( size ) -> getRandom().applyAsInt( 0, size ), min, max )
.orElse( alphabetic().getRange( min, max ) );
}
为什么当这两种方法共享一个实现时,它们的测量结果会如此不同?有什么办法可以改善getMaxLength 的最坏情况吗?
更新
虽然我喜欢 Random 作为来源的理论,但也许这是真的。将我的代码更改为此导致13s 运行,比运行时间更长,是RandomUtils::nextInt 时间的两倍多。
public static DataFactory defaultInstance() {
return new DataFactoryImpl( (a, b) -> a == b ? a : ThreadLocalRandom.current().nextInt(a, b), dict() );
}
【问题讨论】:
-
您是在问为什么 stream 与 parallelStream 实现之间的时间不同,或者为什么两个测试之间的时间不同?如果您制作其中之一stackoverflow.com/help/mcve,则遵循此操作也更简单
-
@xenoterracide 考虑到唯一的区别似乎是调用 getLength 和 getMaxLength,这似乎是显而易见的地方。很遗憾,您没有提供它们。
-
是的,我可以看到,但是要遵循所有级别的间接性非常困难。本质上,您似乎在问为什么 getRange(1,something) 比 getRange(something,something) 慢。可能是因为过滤器在后一种情况下丢弃了很多东西吗?无法判断,我们没有您的数据,而且您的代码非常庞大且高度间接。
-
@xenoterracide,看看stackoverflow.com/help/mcve 事实上,我敢打赌,如果你写了一个,你会很快看到性能差异的来源。
-
似乎我明白你的问题是什么,但总的来说@pvg 是正确的:在发布到 SO 之前,你应该尽可能地简化你的代码。通常在简化过程中,您可能会自己理解问题所在。
标签: java performance java-8 java-stream