The road to Java code performance optimization

1. Preface

Recently, I have been busy participating in the company's new project development. As we are about to go online, we are testing the performance of the system, and in the process, we find some code that has room for performance optimization.So I decided to write an article to record all the performance-optimized case s I've encountered this time and in the future, in the hope that they will be helpful to everyone's coding level.

Source Link:https://github.com/jitwxs/blog-sample/blob/master/Java/performance_optimized

2. Java Foundation

2.1 String Stitching

In our system, there are a large number of caches whose key values are stitched together according to different request parameters, as shown in the following code:

public class LastPriceCache {
    private String keySuffix = "last_price_%s";

    public double getLastPrice(int id) {
        return redisService.get(this.generatorKey(id));
    }

    private String generatorKey(int id) {
        return String.format(keySuffix, id);
    }
}

String splicing has a performance penalty, and if the id value is expected, it can be completely cached in memory, as shown in the following code:

public class LastPriceCache {
    private String keySuffix = "last_price_%s";

    private Map<Integer, String> keyMap = new HashMap<>();

    public double getLastPrice(int id) {
        return redisService.get(this.generatorKey(id));
    }

    private String generatorKey(int id) {
        String result;
        if(!keyMap.containsKey(id)) {
            result = keyMap.get(id);
        } else {
            result = String.format(keySuffix, id);
            keyMap.put(id, result);
        }
        return result;
    }
}

2.2 Packing Type

The problem code in our project is that when dividing using BigDecimal, the precision-preserved decimal variable uses the wrapper type.Unfortunately, I can't reproduce it on my own computer. Maybe the computer configuration of my company is too low, haha.

So I will use the type of packaging to add up for example, although the examples are all rotten streets, they can illustrate the problem.Changing Long from the code below to long results in different time-consuming results.

Long value = 0;
for(int i = 0; i < 100_0000; i ++) {
    value += i;
}

2.3 Stream Merge

In our system, there is a requirement for paging queries for user orders involving filtering/intercepting/sorting of source data, as shown in the following code:

List<Order> sourceOrder = ...;

List<Order> result = sourceOrder.stream().filter(e -> e.getAmount() > 0).collect(Collectors.toList());

if(startId > 0) {
    result = result.stream().filter(e -> e.getId() >= startId).collect(Collectors.toList());
}
if(endId > 0) {
    result = result.stream().filter(e -> e.getId() < endId).collect(Collectors.toList());
}

if(result.size() > limit) {
    result = result.subList(0, limit);
}

Collections.reverse(result);

The introduction of streaming in JDK 1.8 greatly simplifies the code, but improper use can slow performance.In the above code, the streaming operation is abused, and merge operation is fully possible, and subsequent intercept and sort operations can also be integrated in streaming operation, as shown in the following code:

List<Order> sourceOrder = ...;

Stream<Order> stream = sourceOrder.stream();

stream = stream.filter(e -> e.getAmount() > 0);

if(startId > 0) {
    stream = stream.filter(e -> e.getId() >= startId);
}
if(endId > 0) {
    stream = stream.filter(e -> e.getId() < endId);
}

Comparator<Order> comparator = Comparator.comparingLong(Order::getId);
if(!isAsc) {
    comparator = comparator.reversed();
}

List<Order> result = stream.limit(limit).sorted(comparator).collect(Collectors.toList());

3. Tripartite Package

3.1 FastJson preheating

In our project, FastJson is used as the serialization library.FastJson is known to be very fast, but its speed at first serialization is quite daunting, and it is exposed again under pressure conditions.

The problem can be solved by simply loading the following two FastJson classes statically at program startup.

static {
    new ParserConfig();
    new SerializeConfig();
}

3.2 Shallow Copy

Previously, BeanUtils, which used the spring framework directly for shallow copies, were also found to be time consuming in manometry.Using BeanCopier, which is also provided for spring, does a good job.

public class BeanCopierUtils {
    private static final Map<String, BeanCopier> CACHE = new ConcurrentHashMap<>();

    public static void copyProperties(Object source, Object target) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
        copier.copy(source, target, null);
    }

    private static BeanCopier getBeanCopier(Class<?> sourceClazz, Class<?> targetClazz) {
        String key = generatorKey(sourceClazz, targetClazz);
        BeanCopier copier;
        if(CACHE.containsKey(key)) {
            copier = CACHE.get(key);
        } else {
            copier = BeanCopier.create(sourceClazz, targetClazz, false);
            CACHE.put(key, copier);
        }
        return copier;
    }

    private static String generatorKey(Class<?> sourceClazz, Class<?> targetClazz) {
        return sourceClazz + "_" + targetClazz;
    }
}

Tags: Java Spring github JDK

Posted on Sat, 23 May 2020 18:51:24 -0700 by jayd1985