首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么流利的实现比不流利的实现慢?

为什么流利的实现比不流利的实现慢?
EN

Stack Overflow用户
提问于 2012-12-31 19:08:24
回答 4查看 726关注 0票数 2

我编写了一些测试代码,将顺序使用StringBuilderappend()方法8次作为fluent interface的速度与在8行单独调用它的速度进行比较。

如流利:

代码语言:javascript
运行
复制
StringBuilder s = new StringBuilder();
s.append(x)
.append(y)
.append(z); //etc

不流利:

代码语言:javascript
运行
复制
StringBuilder s = new StringBuilder();
s.append(x)
s.append(y)
s.append(z); //etc

每个方法都被调用了一千万次。在每个块之间调用GC。版本的顺序颠倒了,结果是一样的。

我的测试表明,流畅版本的代码大约慢了10% (仅供参考,测试代码中有匹配但不可预测的附加,我为JVM预热等留出了时间)。

这是一个惊喜,因为流利的代码只有一行。

为什么不流利的代码会更快?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-12-31 19:51:05

我怀疑这是Java的一些版本的一个特性。

如果我运行以下命令

代码语言:javascript
运行
复制
public class Main {

    public static final int RUNS = 100000000;

    static final ThreadLocal<StringBuilder> STRING_BUILDER_THREAD_LOCAL = new ThreadLocal<StringBuilder>() {
        @Override
        protected StringBuilder initialValue() {
            return new StringBuilder();
        }
    };

    public static final StringBuilder myStringBuilder() {
        StringBuilder sb = STRING_BUILDER_THREAD_LOCAL.get();
        sb.setLength(0);
        return sb;
    }

    public static long testSeparate(String x, String y, String z) {
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            StringBuilder s = myStringBuilder();
            s.append(x)
                    .append(y)
                    .append(z);
            dontOptimiseAway = s.toString();
        }
        long time = System.nanoTime() - start;
        return time;
    }

    public static long testChained(String x, String y, String z) {
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            StringBuilder s = myStringBuilder();
            s.append(x);
            s.append(y);
            s.append(z);
            dontOptimiseAway = s.toString();
        }
        long time = System.nanoTime() - start;
        return time;
    }

    static String dontOptimiseAway = null;

    public static void main(String... args) {
        for (int i = 0; i < 10; i++) {
            long time1 = testSeparate("x", "y", "z");
            long time2 = testChained("x", "y", "z");
            System.out.printf("Average time separate %.1f ns, chained %.1f ns%n",
                    (double) time1 / RUNS, (double) time2 / RUNS);
        }
    }
}

使用Java 7更新4

代码语言:javascript
运行
复制
Average time separate 49.8 ns, chained 49.0 ns
Average time separate 50.7 ns, chained 49.3 ns
Average time separate 46.9 ns, chained 46.5 ns
Average time separate 46.6 ns, chained 46.4 ns
Average time separate 46.6 ns, chained 46.6 ns
Average time separate 47.6 ns, chained 47.3 ns
Average time separate 46.7 ns, chained 47.2 ns
Average time separate 46.7 ns, chained 47.0 ns
Average time separate 46.0 ns, chained 46.6 ns
Average time separate 46.7 ns, chained 46.3 ns

使用Java 7更新10

代码语言:javascript
运行
复制
Average time separate 50.4 ns, chained 50.0 ns
Average time separate 50.1 ns, chained 50.1 ns
Average time separate 45.9 ns, chained 46.5 ns
Average time separate 46.6 ns, chained 46.7 ns
Average time separate 46.3 ns, chained 46.4 ns
Average time separate 46.7 ns, chained 46.5 ns
Average time separate 46.2 ns, chained 46.4 ns
Average time separate 46.6 ns, chained 46.0 ns
Average time separate 46.4 ns, chained 46.2 ns
Average time separate 45.9 ns, chained 46.2 ns

最初可能看起来有一点偏差,但如果您运行更新10,那么随着时间的推移没有明显的偏差。

票数 6
EN

Stack Overflow用户

发布于 2012-12-31 19:16:13

首先,请对更大的测试重复您的基准测试(例如,10000次而不是8次调用),在多次迭代中运行基准测试,并多次运行整个测试,以查看结果是否一致。

源码行数与结果的速度无关。fluent调用有一个需要处理的返回值,而非fluent调用只是访问一个从未写入的变量,忽略返回值。这可能是一个差异的可能解释,尽管我认为它不应该那么大。

票数 3
EN

Stack Overflow用户

发布于 2012-12-31 19:29:40

我尝试了下面的测试,两种方法的结果非常接近(在一些运行中完全匹配)-所有的方法都是在实际测试之前编译的:

代码语言:javascript
运行
复制
public class Test1 {

    public static void main(String[] arg) {
        //warm up
        for (int i = 0; i < 1_000; i++) {
            method1("" + i);
        }

        for (int i = 0; i < 1_000; i++) {
            method2("" + i);
        }

        //full gc + test method1
        System.gc();
        System.out.println("method1");
        long start = System.nanoTime();
        for (int i = 0; i < 1_000; i++) {
            method1("" + i);
        }
        long end = System.nanoTime();
        System.out.println("method1: " + (end - start) / 1_000_000);

        //full gc + test method2
        System.gc();
        System.out.println("method2");
        start = System.nanoTime();
        for (int i = 0; i < 1_000; i++) {
            method2("" + i);
        }
        end = System.nanoTime();
        System.out.println("method2: " + (end - start) / 1_000_000);
    }

    public static void method1(String seed) {
        StringBuilder sb = new StringBuilder(seed);
        for (int i = 0; i < 10000; i++) {
            sb.append(seed + i)
                    .append(seed + i)
                    .append(seed + i)
                    .append(seed + i)
                    .append(seed + i)
                    .append(seed + i);
        }
        if (sb.length() == 7) {
            System.out.println("ok"); //pretending we are doing something
        }
    }

    public static void method2(String seed) {
        StringBuilder sb = new StringBuilder(seed);
        for (int i = 0; i < 10000; i++) {
            sb.append(seed + i);
            sb.append(seed + i);
            sb.append(seed + i);
            sb.append(seed + i);
            sb.append(seed + i);
            sb.append(seed + i);
        }
        if (sb.length() == 7) {
            System.out.println("ok"); //pretending we are doing something
        }
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14100786

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档