How to answer the question of performance optimization method in the interview?

How to answer the question of performance optimization method in the interview?

Let's chat today. This article mainly focuses on theoretical analysis. Let's look at the laws that Java performance optimization can follow as a whole.

This article focuses on theory and practice. Subsequent articles will use more cases to refine the knowledge points of this article, which is suitable for repeated thinking and induction.

overview

Performance optimization is divided into business optimization and technical optimization according to the type of optimization. The effect of business optimization is also very large, but it belongs to the category of products and management. As programmers, in our daily work, the optimization methods we face are mainly through a series of technical means to complete the established optimization goals. I generally summarize this series of technical means into 7 categories as shown in the figure below:

It can be seen that the optimization method focuses on the planning of computing resources and storage resources. There are many ways to exchange space for time in the optimization method, but it is not advisable to only consider the calculation speed without considering the complexity and space issues. What we have to do is to achieve the optimal state of resource utilization while taking care of performance.

Next, I briefly introduce these 7 optimization directions. If you feel boring, that's okay, the purpose of our article is to let you have a concept of the total score in your mind and an overall understanding of the theoretical basis.

Reuse optimization

When writing code, you will find that there are many repeated codes that can be extracted and made into public methods. In this way, the next time you use it, you don't have to bother to write it again.

This idea is reuse. The above description is an optimization of coding logic, and the same multiplexing situation exists for data access. Whether in life or coding, repetitive things happen all the time. If there is no reuse, work and life will be more tiring.

In a software system, when it comes to data multiplexing, the first thing we think of is buffering and caching. Pay attention to the difference between these two words. Their meanings are completely different. Many students are easy to confuse them. Here is a brief introduction.

Buffer (Buffer), commonly used in the temporary storage of data, and then batch transmission or writing. The sequential method is often used to alleviate frequent and slow random writes between different devices. The buffer is mainly for write operations.

Cache is commonly used in the multiplexing of read data. By caching them in a relatively high-speed area, the cache is mainly aimed at read operations.

Similarly, object pooling operations, such as database connection pools, thread pools, etc., are frequently used in Java. Since the cost of creating and destroying these objects is relatively high, we will also temporarily store these objects after use, so that we don't need to go through the time-consuming initialization operation again when we use them next time.

Computing optimization

parallel execution

The current CPU is developing very fast, and most hardware is multi-core. If you want to speed up the execution of a task, the fastest and best solution is to let it execute in parallel. Parallel execution has the following three modes.

The first mode is multi-machine, which uses load balancing to split traffic or large calculations into multiple parts and process them at the same time. For example, Hadoop breaks up tasks through MapReduce, and multiple machines perform calculations at the same time.

The second mode is to use multiple processes. For example, Nginx adopts the NIO programming model. The Master manages the Worker process in a unified manner, and then the Worker process performs real request proxying, which can also make good use of multiple CPUs in the hardware.

The third mode is to use multithreading, which is also the most exposed to Java programmers. For example, Netty adopts the Reactor programming model and also uses NIO, but it is based on threads. Boss threads are used to receive requests, and then dispatch them to corresponding Worker threads for real business calculations.

Languages ​​like Golang have a more lightweight coroutine (Coroutine). Coroutine is a lighter-weight existence than threads, but it is not yet mature in Java, so I won’t introduce it too much. But in essence, it is also an application for multi-core, so that tasks are executed in parallel.

Synchronous to asynchronous

Another optimization for computing is to change synchronous to asynchronous, which usually involves a change in the programming model. In synchronous mode, the request will be blocked until there is a success or a failure result is returned. Although its programming model is simple, it is particularly problematic when dealing with sudden traffic with skewed time slots, and requests are prone to failure.

Asynchronous operations can easily support horizontal expansion, and can also relieve instantaneous pressure and smooth requests. A synchronous request is like a fist hitting a steel plate; an asynchronous request is like a fist hitting a sponge. You can imagine this process, the latter is definitely elastic and the experience is more friendly.

lazy loading

The last one is to use some common design patterns to optimize business and improve experience, such as singleton mode, proxy mode, etc. For example, when drawing a Swing window, if you want to display more pictures, you can load a placeholder first, and then slowly load the required resources through the background thread, which can avoid the window from freezing.

Result set optimization

Next, let's introduce the optimization of the result set. To give a more intuitive example, we all know that the representation form of XML is very good, so why is there JSON? In addition to being easier to write, an important reason is that its size has become smaller, and its transmission efficiency and analysis efficiency have become higher. Like Google's Protobuf, its size is even smaller. Although the readability is reduced, it can significantly improve efficiency in some high-concurrency scenarios (such as RPC), which is a typical result set optimization.

This is because our current Web services are all C/S models. When data is transmitted from the server to the client, multiple copies need to be distributed. The amount of this data is rapidly expanding. Every time a small part of storage is reduced, there will be a relatively large increase in transmission performance and cost.

Like Nginx, GZIP compression is generally enabled to keep the transmitted content compact. The client only needs a small amount of computing power to decompress easily. Since this operation is distributed, the performance penalty is fixed.

After understanding this principle, we can see the general idea for result set optimization, you should try to keep the returned data as simple as possible. Some fields that are not needed by the client should be removed in the code or directly in the SQL query.

For some businesses that do not require high timeliness but high processing capacity. We should learn from the experience of the buffer zone, minimize the interaction of network connections, and adopt batch processing to increase the processing speed.

The result set is likely to be used twice, and you may add it to the cache, but it still lacks speed. At this time, it is necessary to optimize the processing of the data collection, and use indexes or Bitmap bitmaps to speed up data access.

Resource Conflict Optimization

In our usual development, we will involve a lot of shared resources. Some of these shared resources are stand-alone, such as a HashMap; some are external storage, such as a database row; some are a single resource, such as the Setnx of a key in Redis; some are the coordination of multiple resources, such as transactions, distributed transactions, etc.

There are many performance problems in reality, and there are many problems related to locks. Most of us think of row locks in databases, table locks, various locks in Java, etc. At a lower level, such as CPU command-level locks, JVM instruction-level locks, internal operating system locks, etc., can be said to be everywhere.

Only concurrency can produce resource conflicts. That is, at the same time, only one processing request can obtain shared resources. The way to solve resource conflicts is to add locks. Another example is a transaction, which is essentially a kind of lock.

According to the lock level, locks can be divided into optimistic locks and pessimistic locks. Optimistic locks are definitely more efficient; according to lock types, locks are divided into fair locks and unfair locks. There are some subtleties in scheduling tasks. difference.

The contention for resources will cause serious performance problems, so there will be some research on lock-free queues, which will greatly improve performance.

algorithm optimization

Algorithms can significantly improve the performance of complex businesses, but in actual businesses, they are often variants. As storage becomes cheaper and cheaper, in some businesses where the CPU is very tight, the way of exchanging space for time is often used to speed up the processing speed.

Algorithms belong to code tuning, and code tuning involves many coding skills, requiring users to be very familiar with the API of the language used. Sometimes, the flexible use of algorithms and data structures is also an important part of code optimization. For example, commonly used ways to reduce time complexity include recursion, binary segmentation, sorting, and dynamic programming.

A good implementation has a much greater impact on the system than a poor one. For example, as the implementation of List, LinkedList and ArrayList are several orders of magnitude worse in random access performance; another example, CopyOnWriteList adopts the copy-on-write method, which can significantly reduce lock conflicts in scenarios with more reads and fewer writes. And when to use synchronization and when it is thread-safe also has high requirements on our coding ability.

This part of knowledge requires us to pay attention to accumulation in our usual work, and in the following class hours, we will also select more important knowledge points and explain them interspersed.

Efficient implementation

In normal programming, try to use some components with good design concepts and superior performance. For example, with Netty, there is no need to choose older Mina components. When designing a system, considering performance factors, do not choose time-consuming protocols such as SOAP. For another example, a good syntax analyzer (such as using JavaCC) will be much more efficient than regular expressions.

In short, if the bottleneck of the system is found through test analysis, the key components must be replaced with more efficient components. In this case, the Adapter pattern is very important. This is why many companies like to abstract a layer of their own on top of the existing components; when switching between the underlying components, the upper-level applications have no perception.

JVM optimization

Because Java runs on the JVM virtual machine, many of its features are subject to the constraints of the JVM. Optimizing the JVM virtual machine can also improve the performance of the JAVA program to a certain extent. If the parameters are not configured properly, it may even cause more serious consequences such as OOM.

The currently widely used garbage collector is G1. With few parameter configurations, memory can be efficiently recovered. The CMS garbage collector has been removed in Java 14. Since its GC time is uncontrollable, it should be avoided if possible.

JVM performance tuning involves trade-offs in all aspects, and it often affects the whole body. It is necessary to fully consider the impact of all aspects. Therefore, it is very important to understand some operating principles inside the JVM. It is beneficial for us to deepen our understanding of the code and help us write more efficient code.

summary

The above are the seven general directions of code optimization. Through a brief introduction, we will give you a general understanding of the content of performance optimization. These 7 major directions are the most important directions of code optimization. Of course, performance optimization also includes other content such as database optimization, operating system optimization, and architecture optimization. These are not our focus. In the following articles, we will only do a brief introduction introduce.

 

Guess you like

Origin blog.csdn.net/l688899886/article/details/126631556