为什么String.strip()比String.trim()快5倍于空字符串在Java 11中

问题描述 投票:11回答:3

我遇到了一个有趣的场景。出于某种原因,qazxsw poi对比空白字符串(仅包含空格)明显快于Java 11中的qazxsw poi。

基准

strip()

结果

trim()

显然public class Test { public static final String TEST_STRING = " "; // 3 whitespaces @Benchmark @Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS) @Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS) @BenchmarkMode(Mode.Throughput) public void testTrim() { TEST_STRING.trim(); } @Benchmark @Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS) @Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS) @BenchmarkMode(Mode.Throughput) public void testStrip() { TEST_STRING.strip(); } public static void main(String[] args) throws Exception { org.openjdk.jmh.Main.main(args); } } 胜过# Run complete. Total time: 00:04:16 Benchmark Mode Cnt Score Error Units Test.testStrip thrpt 200 2067457963.295 ± 12353310.918 ops/s Test.testTrim thrpt 200 402307182.894 ± 4559641.554 ops/s ~5次。

虽然对于非空字符串,结果几乎相同:

strip()

怎么会?这是一个错误还是我做错了?


测试环境

  • CPU - Intel Xeon E3-1585L v5 @ 3.00 GHz
  • AS - 主轴7 SP 1 64位
  • JVM - Oracle JDK 11.0.1
  • Bengcha mrk-JMH v 1.19

更新

为不同的字符串添加了更多性能测试(空,空白等)。

基准

trim()

结果

public class Test {

    public static final String TEST_STRING = " Test String ";

    @Benchmark
    @Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS)
    @Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS)
    @BenchmarkMode(Mode.Throughput)
    public void testTrim() {
        TEST_STRING.trim();
    }

    @Benchmark
    @Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS)
    @Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS)
    @BenchmarkMode(Mode.Throughput)
    public void testStrip() {
        TEST_STRING.strip();
    }

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }
}


# Run complete. Total time: 00:04:16

Benchmark        Mode  Cnt          Score         Error  Units
Test.testStrip  thrpt  200  126939018.461 ± 1462665.695  ops/s
Test.testTrim   thrpt  200  141868439.680 ± 1243136.707  ops/s

测试环境是一样的。

只有一个有趣的发现。包含Unicode字符的字符串比@Warmup(iterations = 5, time = 1, timeUnit = SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = SECONDS) @Fork(value = 3) @BenchmarkMode(Mode.Throughput) public class Test { private static final String BLANK = ""; // Blank private static final String EMPTY = " "; // 3 spaces private static final String ASCII = " abc "; // ASCII characters only private static final String UNICODE = " абв "; // Russian Characters private static final String BIG = EMPTY.concat("Test".repeat(100)).concat(EMPTY); @Benchmark public void blankTrim() { BLANK.trim(); } @Benchmark public void blankStrip() { BLANK.strip(); } @Benchmark public void emptyTrim() { EMPTY.trim(); } @Benchmark public void emptyStrip() { EMPTY.strip(); } @Benchmark public void asciiTrim() { ASCII.trim(); } @Benchmark public void asciiStrip() { ASCII.strip(); } @Benchmark public void unicodeTrim() { UNICODE.trim(); } @Benchmark public void unicodeStrip() { UNICODE.strip(); } @Benchmark public void bigTrim() { BIG.trim(); } @Benchmark public void bigStrip() { BIG.strip(); } public static void main(String[] args) throws Exception { org.openjdk.jmh.Main.main(args); } } 'ed更快地获得# Run complete. Total time: 00:05:23 Benchmark Mode Cnt Score Error Units Test.asciiStrip thrpt 15 356846913.133 ± 4096617.178 ops/s Test.asciiTrim thrpt 15 371319467.629 ± 4396583.099 ops/s Test.bigStrip thrpt 15 29058105.304 ± 1909323.104 ops/s Test.bigTrim thrpt 15 28529199.298 ± 1794655.012 ops/s Test.blankStrip thrpt 15 1556405453.206 ± 67230630.036 ops/s Test.blankTrim thrpt 15 1587932109.069 ± 19457780.528 ops/s Test.emptyStrip thrpt 15 2126290275.733 ± 23402906.719 ops/s Test.emptyTrim thrpt 15 406354680.805 ± 14359067.902 ops/s Test.unicodeStrip thrpt 15 37320438.099 ± 399421.799 ops/s Test.unicodeTrim thrpt 15 88226653.577 ± 1628179.578 ops/s

java string performance microbenchmark java-11
3个回答
13
投票

在OpenJDK 11.0.1上trim()(实际上是strip())通过返回一个实施的String.strip()常数来优化剥离空StringLatin1.strip()

String

String(实际上public static String strip(byte[] value) { int left = indexOfNonWhitespace(value); if (left == value.length) { return ""; } )总是分配一个新的String.trim()对象。在你的例子StringLatin1.trim()String所以

st = 3

将在引擎盖下复制数组并创建一个新的len = 3对象

return ((st > 0) || (len < value.length)) ?
        newString(value, st, len - st) : null;

做出以上假设我们可以更新基准以与非空String进行比较,该非空return new String(Arrays.copyOfRange(val, index, index + len), LATIN1); 不应受提及的String优化影响:

String.strip()

运行它显示@Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS) @Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS) @BenchmarkMode(Mode.Throughput) public class MyBenchmark { public static final String EMPTY_STRING = " "; // 3 whitespaces public static final String NOT_EMPTY_STRING = " a "; // 3 whitespaces with a in the middle @Benchmark public void testEmptyTrim() { EMPTY_STRING.trim(); } @Benchmark public void testEmptyStrip() { EMPTY_STRING.strip(); } @Benchmark public void testNotEmptyTrim() { NOT_EMPTY_STRING.trim(); } @Benchmark public void testNotEmptyStrip() { NOT_EMPTY_STRING.strip(); } } strip()之间没有显着差异的非空trim()。奇怪的是,修剪到空的String仍然是最慢的:

String

7
投票

在查看OpenJDK的源代码之后,假设Oracle版本的实现类似,我想可以通过以下事实来解释差异。

  • Benchmark Mode Cnt Score Error Units MyBenchmark.testEmptyStrip thrpt 100 1887848947.416 ± 257906287.634 ops/s MyBenchmark.testEmptyTrim thrpt 100 206638996.217 ± 57952310.906 ops/s MyBenchmark.testNotEmptyStrip thrpt 100 399701777.916 ± 2429785.818 ops/s MyBenchmark.testNotEmptyTrim thrpt 100 385144724.856 ± 3928016.232 ops/s 将尝试找到第一个非空白字符,如果没有找到,只需返回strip
  • ""将永远返回trim

有人可能会说new String(...the substring...)strip更优化,至少在OpenJDK中,因为除非必要,否则它会躲避新对象的创建。

(注意:我没有麻烦地检查这些方法的unicode版本。)


0
投票

是的。在Java 11或更早版本中,似乎.trim()始终创建一个新的String()但strip()返回一个缓存String。您可以测试这个简单的代码并自己证明。

trim

VS

public class JavaClass{
  public static void main(String[] args){
      //prints false
      System.out.println("     ".trim()=="");//CREATING A NEW STRING()
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.