繁体   English   中英

什么更高效:直接设置值还是先检查值是否更改然后再设置?

[英]What is more performant: directly setting a value or first check if the value changed and then set it?

选项1:

List<Object> listObjects = new ArrayList<>();
for(Object object : listObjects){
  object.setValue1("new value");
}

选项 2:

List<Object> listObjects = new ArrayList<>();
for(Object object : listObjects){
  if(!object.getValue1().equals("new value"))
     object.setValue1("new value");
}

以上哪个选项在 CPU 和内存使用方面的性能更高?

附加上下文:假设对象列表非常大,我们每 5 秒循环一次。

我的投票将是选项 1:它更短(忽略 setValue2 调用),如果它是一个对象,则赋值只是加载和存储一个指针,而如果对象非常大,则equal比较可能需要很长时间。

这是我所做的测试(openJDK 11)。 正如我所怀疑的,

  1. “testSetValueNoCheck”和“testSetValueWithCheckUsingStatic”和“testSetValueWithCheckUsingLiteral”几乎相同,这意味着您可以忽略差异。
  2. 有趣的是“第二次testSetValueWithCheckUsingStatic\\testSetValueWithCheckUsingLiteral”的时间总是#1 的一半 我相信这是因为在 Java 中如何为 String 实现 equals - 实际工作的函数“StringLatin1.equals”和“StringUTF16.equals”被注释为“@HotSpotIntrinsicCandidate”,这可能会也可能不会发生。 这也意味着如果 data 不是String ,根据 equals 的实现方式,结果将在上述场景中有所不同。

“第二次”案例与您所写的内容相关:

附加上下文:假设对象列表非常大,我们每 5 秒循环一次。

因此,总的来说,我确实同意评论中的建议 - 在需要时担心性能。 就我个人而言,时间是每次我完成模块并进行单元测试,进行性能测试。 这需要一天(如果你有自动化,甚至更少),但我从来没有发现它会适得其反。

输出:

testSetValueNoCheck:39.9644

testSetValueWithCheckUsingStatic:40.4051

testSetValueWithCheckUsingStatic 第二次:13.7888

testSetValueWithCheckUsingLiteral: 33.6455

testSetValueWithCheckUsingLiteral 第二次:18.5136

代码:

    class SetValueTest {
    class MyObject {
        private String _val;
        MyObject(final String val) {
            _val = val;
        }
        
        String getValue() {
            return _val;
        }
        
        void setValue(final String val) {
            _val = val;
        }
        
    }
    
    ArrayList<MyObject> getData() {
        final ArrayList<MyObject> data = new ArrayList<>();
        for(int i = 0; i < 1000000; ++i) {
            data.add(new MyObject(String.format("%d", i)));
        }
        return data;
        
    }
    
    SetValueTest() throws InterruptedException {
        long start = 0L, end = 0L; 
        
        final ArrayList<MyObject> data1 = getData();
        
        start = System.nanoTime();
        testSetValueNoCheck(data1);
        end = System.nanoTime();
        System.out.println("testSetValueNoCheck: " + (end - start) / 1e6);
        
        final ArrayList<MyObject> data2 = getData();
        
        Thread.currentThread().sleep(1000);
        
        start = System.nanoTime();
        testSetValueWithCheckUsingStatic(data2);
        end = System.nanoTime();
        System.out.println("testSetValueWithCheckUsingStatic: " + (end - start) / 1e6);
        
        start = System.nanoTime();
        testSetValueWithCheckUsingStatic(data2);
        end = System.nanoTime();
        System.out.println("testSetValueWithCheckUsingStatic second time: " + (end - start) / 1e6);
        
        
        final ArrayList<MyObject> data3 = getData();
        
        Thread.currentThread().sleep(1000);         
        
        start = System.nanoTime();
        testSetValueWithCheckUsingLiteral(data3);
        end = System.nanoTime();
        System.out.println("testSetValueWithCheckUsingLiteral: " + (end - start) / 1e6);
        
        start = System.nanoTime();
        testSetValueWithCheckUsingLiteral(data3);
        end = System.nanoTime();
        System.out.println("testSetValueWithCheckUsingLiteral second time: " + (end - start) / 1e6);
        
    }
    
    void testSetValueNoCheck(List<MyObject> objs) {
        for (MyObject obj : objs) {
            obj.setValue(NewValue);
        }
    }
    
    void testSetValueWithCheckUsingLiteral(List<MyObject> objs) {
        for (MyObject obj : objs) {
            if (!obj.getValue().equals("new Value")) {
                obj.setValue("new Value");
            }
        }
    }       
    
    static final String NewValue = "new Value";
    
    void testSetValueWithCheckUsingStatic(List<MyObject> objs) {
        for (MyObject obj : objs) {
            if (!obj.getValue().equals(NewValue)) {
                obj.setValue(NewValue);
            }
        }
    }
    
}

第二轮

  • 变化:

--- 更改了类,以便每个类运行一次,以便垃圾收集\\内存不会干扰读数

--- sleep 改为 5 秒以模拟实际场景

--- 每个测试运行 10 次,平均值如下:

testSetValueWithCheckUsingStatic:平均第一次:27.728210 第二次:10.979410

testSetValueWithCheckUsingLiteral:平均第一次:23.161670 第二次:12.896330

testSetValueNoCheckStatic:平均第一次:20.294100 第二次:21.679260

testSetValueNoCheckLiteral:平均第一次:29.137620 第二次:19.812040

    static class SetValueTest {
    class MyObject {
        private String _val;
        MyObject(final String val) {
            _val = val;
        }
        
        String getValue() {
            return _val;
        }
        
        void setValue(final String val) {
            _val = val;
        }
        
    }
    
    ArrayList<MyObject> getData() {
        
        final ArrayList<MyObject> data = new ArrayList<>();
        for(int i = 0; i < 1000000; ++i) {
            data.add(new MyObject(String.format("%d", i)));
        }
        return data;
        
    }
    
    private final static int SleepTime = 5000;
    private static final String NewValue = "new Value";
    
    SetValueTest(final int test) throws InterruptedException {

        final ArrayList<Double> firstTimes = new ArrayList<>();
        final ArrayList<Double> secondTimes = new ArrayList<>();
        String testName = "";
        final int times = 10;
        for (int i = 0; i < times; ++i) {
            double firstTime = 0, secondTime = 0;
            final ArrayList<MyObject> data = getData();
            switch (test) {
            case 1:
                testName = "testSetValueWithCheckUsingStatic";
                firstTime = testSetValueWithCheckUsingStatic(data);
                secondTime = testSetValueWithCheckUsingStatic(data);
                break;
            case 2:
                testName = "testSetValueWithCheckUsingLiteral";
                firstTime = testSetValueWithCheckUsingLiteral(data);
                secondTime = testSetValueWithCheckUsingLiteral(data);
                break;
            case 3:                 
                testName = "testSetValueNoCheckStatic";
                firstTime = testSetValueNoCheckStatic(data);
                secondTime = testSetValueNoCheckStatic(data);
                break;
            case 4:                 
                testName = "testSetValueNoCheckLiteral";
                firstTime = testSetValueNoCheckLiteral(data);
                secondTime = testSetValueNoCheckLiteral(data);
                break;
            }
            firstTimes.add(firstTime);
            secondTimes.add(secondTime);
        }
        
        double firstTimeTotal = 0, secondTimeTotal = 0;
        System.out.println("Test: " + testName);
        for (int time = 0; time < times; ++time) {
            System.out.println(String.format("First Time: %f Second Time: %f", firstTimes.get(time), secondTimes.get(time)));
            firstTimeTotal += firstTimes.get(time);
            secondTimeTotal += secondTimes.get(time);
        }
        System.out.println(String.format("Average First Time: %f Second Time: %f", firstTimeTotal / times, secondTimeTotal / times));
        
        
    }
    
    double toMilliseconds(final long start, final long end) {
        return ((end - start) / 1e6);
    }
    
    double testSetValueNoCheckStatic(List<MyObject> objs) throws InterruptedException {
        
        long start = 0L, end = 0L; 
        start = System.nanoTime();
        for (MyObject obj : objs) {
            obj.setValue(NewValue);
        }
        end = System.nanoTime();
        Thread.currentThread().sleep(SleepTime);
        return toMilliseconds(start, end);
        
    }
    
    double testSetValueNoCheckLiteral(List<MyObject> objs) throws InterruptedException {
        
        long start = 0L, end = 0L; 
        start = System.nanoTime();
        for (MyObject obj : objs) {
            obj.setValue("new Value");
        }
        end = System.nanoTime();
        Thread.currentThread().sleep(SleepTime);
        return toMilliseconds(start, end);          
        
    }
    
    
    double testSetValueWithCheckUsingLiteral(List<MyObject> objs) throws InterruptedException {
        long start = 0L, end = 0L; 
        start = System.nanoTime();
        
        for (MyObject obj : objs) {
            if (!obj.getValue().equals("new Value")) {
                obj.setValue("new Value");
            }
        }
        end = System.nanoTime();
        Thread.currentThread().sleep(SleepTime);
        return toMilliseconds(start, end);          
    }       

    
    double testSetValueWithCheckUsingStatic(List<MyObject> objs) throws InterruptedException {
        long start = 0L, end = 0L; 
        start = System.nanoTime();
        
        for (MyObject obj : objs) {
            if (!obj.getValue().equals(NewValue)) {
                obj.setValue(NewValue);
            }
        }
        end = System.nanoTime();
        Thread.currentThread().sleep(SleepTime);
        return toMilliseconds(start, end);          
        
    }
    
}

实际输出:

测试:testSetValueWithCheckUsingStatic第一次:42.110700第二次:21.398500第一次:37.834800第二次:10.124700第一次:18.263600第二回:8.967400第一次:16.655500第二回:7.146000第一次:18.729900第二回:8.753300第一次:17.513100第二次: 14.622800 第一次:24.000300 第二次:9.534700 第一次:61.357000 第二次:11.677700 第一次:26.832500 第二次:10.160400 第一次:13.07 第二次:10.97 1986

测试:testSetValueWithCheckUsingLiteral第一次:54.417700第二次:22.161600第一次:20.950000第二次:14.621500第一次:31.008100第二次:13.631600第一次:14.052500第二次:10.490900第一次:20.904800第二回:8.727700第一次:13.338300第二次: 13.519100 第一次:18.740800 第二次:13.030500 第一次:14.959100 第二次:15.166100 第一次:25.593000 第二次:8.041300 第一次:17.05.0736 第二次:17.057362

测试:testSetValueNoCheckStatic第一次:34.467700第二次:33.953200第一次:15.307500第二次:14.245300第一次:34.042700第二次:31.824600第一次:11.989300第二次:12.266800第一次:15.556500第二次:24.501000第一次:12.314800第二次: 16.539600 第一次:14.333300 第二次:14.500300 第一次:11.876900 第二次:21.599900 第一次:39.020400 第二次:31.754800 第一次:105.206 次:105.206 次:105.206 次:105.206

测试:testSetValueNoCheckLiteral第一次:49.325300第二次:27.528700第一次:26.355500第二次:27.800100第一次:23.862700第二次:15.576400第一次:25.133000第二次:16.290500第一次:28.379000第二次:18.192400第一次:83.170600第二次: 18.183800 第一次:13.570100 第二次:14.341100 第一次:11.793900 第二次:13.960000 第一次:16.238900 第二次:30.233200 第一次:13.204 次:23.204 107 次:13.204

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM