Improve the performance of Java IO operations

xiaoxiao2021-03-06  35

First, the general concept of performance optimization

It is generally believed that the Java program is always slower than the C procedures, and most people may have heard too much. In fact, the situation is much more complicated than those who are old. Many Java programs are really slow, but the speed is slow is not inherent characteristics of all Java programs. Many Java programs can reach the efficiency of similar programs in C or C , but only when designers and programmers pay close attention to performance issues throughout the development process.

The main purpose of this article is to discuss how to optimize the performance of Java IO operations. Many applications spend a lot of run time on network or file IO operations, and the design of poor IO code may be twice as much as carefully adjusted IO code.

Some concepts are always lifted again and again. The examples of this paper are expanded around the optimization of IO applications, but the basic principles are equally applicable to other performance.

For performance optimization, the most important principle may be: testing as soon as possible, often testing. I don't know the root of performance issues, I can't effectively adjust performance, and many programmers have been in vain because of unrivained performance issues. It takes more than one percent by one percent of one percent of the total run time, and the application performance cannot exceed one percent. Therefore, you should avoid guess, but use performance test tools, such as some code analysis tools or logs with time information, find the most time-consuming place in the application, then focus on optimizing the hotspots of these programs. After the performance is completed, the test should be tested again. Test not only helps programmers to focus on those most important code, but also show that performance adjustments have achieved success.

During adjusting the program performance, the data that needs to be tested may have a lot, such as running total time, memory occupation average, memory occupancy peak, program throughput, request delay time, and object creation. It should be concerned about which factors should be concerned, which is related to specific situations and requirements for performance. Most of these data can be tested through some excellent commercial analysis tools, however, not necessarily there must be expensive code analysis tools to collect useful performance data.

The performance data collected herein is only for running time, and the tools used in test are similar to the following Timer class (which can be easily extended to allow it to support Pause () and restart (). Log Output statements with time information affect test results, because these statements also have to create objects and execute IO operations, Timer allows us to collect time information without such statements.

Public class timer {// A simple "stopwatch" class with accuracy of milliseconds. private long startTime, endTime; public void start () {startTime = System.currentTimeMillis ();} public void stop () {endTime = System.currentTimeMillis ();} public long getTime () {return endTime - startTime;}}

One of the common causes of Java performance issues is excessively created temporary objects. Although the new Java virtual machine effectively reduces performance impacts while creating many small objects, this fact is still not changed in this fact that the object creation is expensive. String classes are often the biggest culprit in the String class because of the string objects, because each time a String object is changed, you will create one or more new objects. It can be seen that the second principle of improving performance is to avoid excessive object creation operations.

Second, IO performance optimization

Many applications should perform large-scale data processing, and IO operations are places where the subtle changes will lead to large performance differences. The examples of this article come from performance optimization of a text processing application, which analyzes and processes a large number of texts. In a text processing application, the time to read and process the input text is critical to optimize the measures used by the application to provide a good example of the performance optimization principles indicated above. One of the most important reasons that affect Java IO performance is to use a single-character IO operation using a single character IO operation, that is, using the inputStream.Read () and Reader.Read () method each time a character is read each time. Java's single-character IO operation inherits from the C language. In a C language, a single character IO operation is a common operation, such as repeated calling getc () reads a file. The C language single-character IO operation is high, because Getc () and PUTC () functions are implemented in the form of macros, and support with buffered file access, so the two functions can only be completed. In Java, the situation is completely different: for each character, not only one or more methods are called, but more importantly, if any type of buffer is not used, it is necessary to have a system call. Although a Java program that relies Read () may be the same as the performance, function, but both in performance cannot be compared. Fortunately, Java offers several simple ways to help us get better IO performance.

Buffers can be implemented in one of two ways: using standard BufferedReader and BufferedInputStream classes, or using block reading methods to read a large piece of data at a time. The former is quick and easy, can effectively improve performance, and only a small amount of increased code, and there are fewer chances. The latter is also writing code, and the complexity is slightly improved - of course, it is not difficult, but it can get better results.

To test the efficiency of different IO operations, this article uses six applets, which read hundreds of files and analyzes each character. Table 1 shows the running time of these six programs, testing five common Linux Java virtual machines: Sun 1.1.7, 1.2.2 and 1.3 Java virtual machines, IBM 1.1.8 and 1.3 Java virtual machines.

These six programs are:

Rawbytes: Use fileInputstream.read () to read a byte each time. Rawchars: Use FileReader.Read () to read a character each time. BUFFEREDIS: With bufferedinputstream package fileInputStream, use read () to read a byte data each time. Bufferedr: With bufferedReader package fileReader, use read () to read a character each time. Selfbufferedis: Use FileInputStream.read (byte []) to access data from the buffer each time 1 k data. Selfbufferedr: Use FileReader.Read (char []) to access 1 K data, access data from the buffer.

Table Sun 1.1.7 IBM 1.1.8 Sun 1.2.2 Sun 1.3 IBM 1.3 RawBytes 20.6 18.0 26.1 20.70 62.70 RawChars 100.0 235.0 174.0 438.00 148.00 BufferedIS 9.2 1.8 8.6 2.28 2.65 BufferedR 16.7 2.4 10.0 2.84 3.10 SelfBufferedIS 2.1 0.4 2.0 0.61 0.53 SelfBufferedR 8.2 0.9 2.7 1.12 1.17

Table 1 is to adjust the total time of hundreds of files after adjusting the Java VM and program launch configuration. From the table one we can get a few obutable conclusions: InputStream is efficient than Reader. A char uses two bytes to save characters, and Byte only needs one, so the memory that saves characters with Byte and less machine instructions that need to be executed. More importantly, use Byte to avoid Unicode conversion. Therefore, if possible, try to use BYTE to replace char. For example, if an application must support internationalization, you must use char; if you read from an ASCII data source (such as an HTTP or MIME header), or you can determine that the input text is always English, the program can use Byte. There is no buffered character IO is very slow. Character IO has not been efficient, if there is no buffer, the situation is worse. Therefore, in programming practice, at least the stream should be buffered, which allows IO performance by more than 10 times. Blocks with buffered blocks are fast than buffering stream character Io. For character IO, although buffering flow avoids system call overhead when reading characters, it still needs to be called once or more. The buffered block IO is 2 to 4 times faster than the buffer IO, 4 to 40 times faster than the buffered IO.

It is not easy to see from the table that character IO may offset the advantage of the speed of fast Java VM. In most performance tests, IBM 1.1.8 Linux Java VM has twice as fast as Sun 1.1.7 Linux Java VM, but in the test of Rawbytes and Rawchars, the results show that the two are almost slow, they spend system calls The additional time overhead covered with the speed advantage of faster Java VM.

The block IO has another unique advantage. The buffered character IO sometimes has more demands between the coordination between components, bringing more error chances. Many times, the IO operation in the application is complete by a component, and the application passes a Reader or InputStream to the component, then the IO component processing stream. Some IO components may incorrectly assume that the stream it operates is a buffered stream, but not in the documentation, or although the IO component illustrates this in the document, the developer of the application is Failed to pay attention to this. In these cases, the IO operation will not be buffered in the expected, thereby bringing serious performance problems. If the block IO is changed, this kind of situation is impossible (therefore, when designing software components, it is best to make components to be misuse, not to rely on documentation to ensure the correct use of components).

As can be seen from the above simple test, a simple task is accomplished with the most direct way, such as reading text, may be slower than 40 to 60 times more than careful. In these tests, the program performs some calculations when extracting and analyzing each character. If the program only copies the data from a stream to another stream, the performance difference between the non-buffered character IO and block IO will be more pronounced, and the performance of block IO will reach 300 to 500 times of non-buffered character IO.

Third, tested again

Performance adjustments must be processed repeatedly because secondary performance issues often cannot be revealed before the main performance problem is solved. In an example of a text processing application, the initial analysis showed that the program took the most of the time the time of reading characters, plus the buffer function has dramatic improvement. Only after the program solves the main performance bottleneck (character IO), the remaining performance hotspot is displayed. The second analysis of the program is displayed, and the program takes a lot of time on the creation of the String object, and it seems to create more than one String object for each word in the input text.

The text analysis application in this paper uses modular design, and users can achieve the expected goals combined with multiple text processing operations. For example, the user can combine using a word identifier component (read input character and organizing them into words) and lowercase letters converter components (transitioning words into lowercase letters), and a restore device component (convert words into their basic Form, for example, converting Jumper and Jumped into JUMP). Although modular construction has a significant advantage, this processing is a negative impact on performance. Since the interface between the components is fixed (each component is used as an input as an input, and the other String) is output, there may be some repetitive operations between the components. If several components are often used together, optimize these situations is worth it.

In this text processing system, it can be seen from the actual usage, and the user is almost always used after using the word identifier component. The word identifier analyzes each character, looking for a word boundary, and populates a word buffer. After identifying a complete word, the word identifier component will create a String object for it. The next component in the call chain is lowercase letters converter components, which will call String.TolowerCase () on the String String, creating another String object. For each word in the input text, the two components will generate two String objects. Since the word identifier component is used frequently together, an optimized lowercase letterword identifier can be added, this identifier has the function of two components, but only creates a String object for each word. It is conducive to improve performance. Table 2 shows the test results:

Table 2 SUN 1.1.7 IBM 1.1.8 Sun 1.2.2 Sun 1.3 IBM 1.3 A Word Identification 23.0 3.6 10.7 2.6 2.9 B Word Identification Small-Writing Letters Translation 39.6 6.7 13.9 3.9 3.9 C Combined Word Identity and Small-Writing Letters Translation 29.0 3.8 12.9 3.1 3.1 Temporary string creation time (BC) 10.6 2.9 1.0 0.8 0.8

We can get a few useful discovery from the table.

For Java VM 1.1, simple optimization is increasingly increasing performance: approximately 25% to 45%. The last line shows that the creation of a temporary String object takes up a performance add value of 60% to 90% between programs A and program B. In addition, as shown in several other test items, IBM Java VM 1.1 runs fast than Sun Java VM 1.1. For Java VMs of 1.2 and 1.3, the performance difference between the two versions is no longer so big, only 10% to twenty-five percent, which is equivalent to creating a period of time spent on the temporary String object. This result shows that in terms of creating an object instance, the higher version of Java VM does improve efficiency, but too many objects creation operations are still worth noting. For such a large number of small objects, the 1.3 version of Java VM is much more than the Java VM of 1.1 and 1.2.

Performance optimization is a need for repeated work. It is worthwhile to collect performance data in the early stages of development work, as this can be found and adjust performance hotspots as soon as possible. With some relatively simple improvements, such as adding buffers for IO operation, or replacing CHAR when appropriate, often dramatically improve the performance of the application. In addition, there is a large performance difference between different VMs, simply replacing a fast Java VM, which may make the program's performance to the expected goal out of a big step. Reference:

Sun China website: Adjust Java I / O performance

转载请注明原文地址:https://www.9cbs.com/read-40633.html

New Post(0)