Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java11中的空字符串的String.strip()比String.trim()快5倍_Java_String_Performance_Microbenchmark_Java 11 - Fatal编程技术网

为什么Java11中的空字符串的String.strip()比String.trim()快5倍

为什么Java11中的空字符串的String.strip()比String.trim()快5倍,java,string,performance,microbenchmark,java-11,Java,String,Performance,Microbenchmark,Java 11,我遇到了一个有趣的场景。由于某些原因,Java 11中的strip()对空字符串(仅包含空格)的速度明显快于trim() 基准 public class Test { public static final String TEST_STRING = " "; // 3 whitespaces @Benchmark @Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS) @Measureme

我遇到了一个有趣的场景。由于某些原因,Java 11中的
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);
    }
}
@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);
    }
}
结果

# 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
# 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
显然,
strip()
trim()
好5倍

尽管对于非空字符串,结果几乎相同:

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
为什么?这是一个错误还是我做错了


测试环境

  • CPU-英特尔至强E3-1585L v5@3.00 GHz
  • 操作系统-Windows 7 SP 1 64位
  • JVM-Oracle JDK 11.0.1
  • Benchamrk-JMH v 1.19

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

基准

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);
    }
}
@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);
    }
}
结果

# 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
# 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
测试环境是一样的


只有一个有趣的发现。包含Unicode字符的字符串比strip()的速度快。在查看OpenJDK的源代码后,假设Oracle版本的实现是类似的,我认为差异可以通过以下事实来解释:

  • strip
    将尝试查找第一个非空白字符,如果找不到,只返回
    “”
  • trim
    将始终返回
    新字符串(…子字符串…)
有人可能会说,
strip
trim
优化了一点点,至少在OpenJDK中是这样,因为除非必要,否则它会避开新对象的创建


(注意:我没有费心检查这些方法的unicode版本。)

在OpenJDK 11.0.1上
String.strip()
(实际上
stringlati1.strip()
)通过返回一个插入的
字符串
常量,将剥离优化为空
字符串

public static String strip(byte[] value) {
    int left = indexOfNonWhitespace(value);
    if (left == value.length) {
        return "";
    }
String.trim()
(实际上
StringLatin1.trim()
)总是分配一个新的
String
对象。在您的示例中,
st=3
len=3
so

return ((st > 0) || (len < value.length)) ?
        newString(value, st, len - st) : null;
根据上述假设,我们可以更新基准以与非空的
字符串进行比较,该字符串不应受到所述
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()
之间的显著差异。奇怪的是,修剪成空的
字符串仍然是最慢的:

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

是的。在Java11或更早版本中,.trim()似乎总是创建一个新字符串(),但strip()返回一个缓存字符串。您可以测试这个简单的代码,并自己验证它

public class JavaClass{
  public static void main(String[] args){
      //prints false
      System.out.println("     ".trim()=="");//CREATING A NEW STRING()
  }
}
vs


strip()
较新…(不使用
getChar
{unicode},只检查尾随字符中是否有空字符串,返回
{literal}而不是
新字符串(字节)
),感谢您的解释!我有点想知道为什么JDK开发人员没有像
strip()
那样优化
trim()
。5x是一个巨大的性能差异。