从反射中迁移现有代码,它的运行速度是原来的两倍



有人给了我一段代码,让它"不使用反射",因为它太慢了。代码目前使用反射来获取205个字段的值,将其全部附加到StringBuilder中并返回。它看起来像这样:

public String reflection()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("ExecutionEvent(");
        // This will be very slow. We need to hard code the string
        // encoding for this event type
        // For now I am using reflection to quickly flesh out the framework
        // code.
        // TODO: fix this to not use reflection
        String delim = "";
        for (Field f : this.getClass().getFields())
        {
            try
            {
                Object o = f.get(this);
                if (f.getType() == String.class)
                {
                    // We wrap values with double quotes so cannot contain a double quote within a value.
                    // Change to single quote.
                    String value = o == null ? "" : o.toString().replace(""", "'");
                    // value = value.replace("'", "\'");
                    // value = value.replaceAll("\\", "");
                    sb.append(delim);
                    sb.append(""");
                    sb.append(value);
                    sb.append(""");
                }
                else
                {
                    sb.append(delim);
                    String value = o.toString();
                    sb.append(value);
                }
                delim = ",";
            }
            catch (Exception ex)
            {
                logger.error("Cannot get value for field. {}", ex.getMessage());
            }
        }
        if (sb.toString().contains("\"))
        {
            sb.replace(sb.indexOf("\"), sb.indexOf("\") + 1, "");
        }
        sb.append(")");
        return sb.toString();
    }

我把这段代码改成了:

    public String noReflection() {
        StringBuilder sb = new StringBuilder();
        sb.append("ExecutionEvent(");
        String delim = "";
        for (Object o : getFields()) {
            if(o instanceof String || o == null){
                o = o == null ? "" : ((String) o).replace(""", "");
                sb.append(String.format("%s"%s"", delim, o));
            }
            else
                sb.append(String.format("%s%s", delim, o.toString()));
            delim = ",";
        }
        if (sb.toString().contains("\")) {
            sb.replace(sb.indexOf("\"), sb.indexOf("\") + 1, "");
        }
        sb.append(")");

        return sb.toString();
    }

for循环中的getFields()方法引用我创建的另一个方法:

private List<Object> getFields(){
        List<Object> values = new ArrayList<Object>();
        values.add(announcementDate);
        //Another ~200 values.add(fieldName);
        values.add(whenIssuedIndicator);
        return values;
    }

我并排运行了这两种方法,它们是我唯一要改变的东西。我使用现有代码的基准测试来测量速度:

try ( ResultSet resultSet = stmt.executeQuery(sqls.get(8));) {
                        StopWatch timer = new StopWatch();
                        timer.start();
                        while (resultSet.next()) {
                            processRow(resultSet, runDate);
                            count++;
                            if (count % 100000 == 0) {
                                logger.info(feedName+ " | "+ new Object() {}.getClass().getEnclosingMethod().getName()+ " | OceanOrderFeed processed {} orders",
                                decimalFormat.format(count));                                                               
                            }
                        }
                        timer.stop();
                        int hours = (int) (timer.getTotalTimeSeconds() / 3600);
                        int minutes = (int) ((timer.getTotalTimeSeconds() - (hours * 3600)) / 60);
                        int seconds = (int) ((timer.getTotalTimeSeconds() - (hours * 3600) - (minutes * 60)));
                        logger.info(
                                feedName+ " | "+ new Object() {}.getClass().getEnclosingMethod().getName()
                                        + " | OceanOrderFeed completed. Processed {} orders in {} Hours : {} Minutes : {} Seconds",
                                decimalFormat.format(count), hours, minutes, seconds);
                    }

基准测试给出了以下结果:

No reflection
Processed 1,000,000 orders in 0 Hours : 8 Minutes : 49 Seconds
Processed 1,000,000 orders in 0 Hours : 9 Minutes : 3 Seconds
Processed 1,000,000 orders in 0 Hours : 10 Minutes : 11 Seconds
Reflection
Processed 1,000,000 orders in 0 Hours : 4 Minutes : 46 Seconds
Processed 1,000,000 orders in 0 Hours : 4 Minutes : 27 Seconds
Processed 1,000,000 orders in 0 Hours : 4 Minutes : 34 Seconds

使用反射全面更快。是否有一个问题,在代码我已经发布,导致的东西,应该是更快更慢?

问题是,我在我的循环中使用String.format()时,我应该在原始代码中使用StringBuilder.append()

原因是在当前的实现String。格式先使用正则表达式解析输入,然后填充参数。另一方面,带加号的串联得到了优化使用javac(而不是JIT)并使用StringBuilder。直接添加。

进一步阅读。