Binary program size optimizer based on loop folding
Through the loop folding binary program volume optimizer, performance profiling data is used to finely control the optimization strategy, which solves the problem of excessive program size caused by aggressive loop unrolling and achieves the reduction of program size without affecting performance.
Patent Information
- Application Number
- CN202210154571.5
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2022-02-21
- Publication Date
- 2025-09-05
- Estimated Expiration
- 2042-02-21
AI Technical Summary
In the prior art, aggressive loop unrolling strategies result in excessively large binary program sizes, and inaccurate mapping of existing performance profiling data leads to poor optimization results.
Provides a binary program size optimizer based on loop folding. It collects performance profiling data, calculates the loop folding level, and performs loop folding, including continuous memory access and iterative loop folding, to generate optimized executable files.
Without affecting the original performance, the binary program size is reduced, the optimization threshold is lowered, and a more refined optimization strategy is implemented. The optimized program size is smaller and the performance remains similar.
Smart Images

Figure CN114546401B_ABST
Abstract
Description
Technical Field
[0001] The present invention relates to the field of computer technology, and in particular to a binary program volume optimizer based on loop folding. Background Art
[0002] Program code size is increasing. This is partly due to the increasing complexity of program functionality and partly due to the increasing number of libraries and environments they rely on. Furthermore, many programs employ an iterative software development model, with new features constantly being added. This makes excessively large programs a common occurrence. Platforms often impose certain restrictions on the disk space and memory required for programs. Oversized programs not only consume limited storage resources but also compromise program efficiency and can even cause system failures. For example, embedded devices have very limited storage space and are difficult to scale. Software is often optimized for embedded platforms to reduce package size. On servers and supercomputers, large-scale programs often have even larger code sizes. For example, a PHP program with tens of millions of lines of code running on Facebook servers can be compiled into hundreds of megabytes of machine code. A parallel version of a program implemented using OpenMP is significantly larger than its serial counterpart. Large files not only consume significant storage space but also make transmission between servers or supercomputer nodes more difficult. However, simply adding memory or bandwidth to these platforms is often difficult, partly because space on highly integrated chips is very limited, and partly because high transistor prices lead to rapidly rising costs. Furthermore, smaller programs mean fewer instructions are executed, which reduces pressure on the L1 instruction cache and instruction TLB, thereby improving program performance and reducing energy consumption.
[0003] Compilers provide a range of default optimization levels to meet different needs. Optimizations that improve performance often increase code size, while optimizations that reduce code size can also hurt performance. Loop unrolling is often considered a performance optimization that increases code size. A common method used by compilers, loop unrolling replicates the loop body several times to reduce the number of iterations, thereby reducing loop overhead. For larger loops, loop unrolling can benefit from instruction parallelism, register allocation, memory hierarchy, and can exploit hardware features to combine instructions. However, the resulting code may introduce a large number of intermediate variables, which can lead to register spills, discontinuous memory accesses, and instruction cache overflows. There are also code size optimizations that have a minimal impact on performance. A common solution is to reduce common or dead code segments. Common subexpression elimination searches for instances of identical expressions and analyzes whether they are worth replacing with a single variable holding the calculated value. Dead code elimination attempts to remove code that does not affect the program's results. These methods all aim to reduce program size by removing unused code or redundant computation.
[0004] Loop unrolling is a significant factor affecting program size, and different compilers have different strategies for loop unrolling. GCC doesn't unroll most loops even at the -O3 optimization level unless the user specifies -funroll-loops or uses profiling optimizations. By default, GCC only fully unrolls loops that are small and have a known loop count at compile time. In contrast, LLVM unrolls loops even at the -O1 optimization level. While such aggressive loop unrolling is acceptable in modern compilers and generally improves performance, it's difficult to say which strategy is best for existing programs, as the optimization of individual functions can vary depending on the final compiled application. However, such aggressive unrolling exacerbates the code size problem. Infrequently executed loops are fully unrolled, increasing code size without improving performance.
[0005] While most modern compilers support Profiling Guided Optimization (PGO), it currently has some limitations and drawbacks. The accuracy of performance data is crucial for PGO and significantly impacts its effectiveness. Not only is the actual environment in which the data is collected important, but how the performance data is mapped to the code is also crucial. GCC, LLVM, and other optimizers map performance data back to the compiler's intermediate representation (IR) or source code. However, the binary code used for performance testing often differs from the original source code, making it impossible to accurately map the performance data. Summary of the Invention
[0006] In view of the above-mentioned defects of the prior art, the technical problem to be solved by the present invention is to provide a binary program volume optimizer based on loop folding, which solves the problem of excessive code size caused by aggressive loop unrolling strategies. The code size can be reduced with little impact on the performance of the original program, and the optimization strategy can be finely controlled using performance profiling data. In addition, the binary program can be optimized without recompiling from the source code.
[0007] To achieve the above objectives, the present invention provides a binary program volume optimizer based on loop folding, the execution of which includes the following steps:
[0008] Step 1: Run the program and collect performance analysis data;
[0009] Step 2: Input the binary program and performance analysis data into the optimizer;
[0010] Step 3: Output the optimized program.
[0011] Furthermore, after the binary program and performance analysis data are input into the optimizer, the optimization steps of the optimizer include:
[0012] Step 1-1: parse the performance profile data to obtain time and instruction information about the loop code;
[0013] Step 1-2, calculate the loop folding level of each loop;
[0014] Steps 1-3: perform loop folding, including loop folding for continuous memory access and loop folding for iteration;
[0015] Steps 1-4: Generate optimized executable files.
[0016] Furthermore, the calculation of the loop folding level of each loop specifically includes: calculating three levels of loop folding levels, where the high-level fully folded loop body only contains a loop with one iteration, the middle level reduces the expansion factor by half, and the lowest level keeps the original loop unchanged. Specifically, the following strategy is used to determine the folding level corresponding to the loop:
[0017] S=0.5*time / total_loop_time+0.5*exec_num / total_exe_num
[0018] Among them, S is the final folding score, time is the wall running time of the loop, total_loop_time is the total running time of all loops, exec_num is the number of executions of the loop, and total_exe_num is the total number of executions of all loops; if the score is less than 0.25, it is folded at the highest level; if the score is greater than 0.25 and less than 0.5, it is folded at the middle level; if the score is greater than 0.5, it is not folded.
[0019] Furthermore, the loop folding of continuous memory accesses includes:
[0020] Step 2-1: Disassemble the binary file, identify all loops, and find iterative registers with consecutive memory accesses.
[0021] Step 2-2, scheduling an iteration register update instruction to update the memory address associated with the iteration register;
[0022] Step 2-3: Calculate the loop expansion coefficient and step size, and then perform correlation analysis and instruction replacement.
[0023] Furthermore, the loop folding of iterations includes:
[0024] Step 3-1: Disassemble the binary file, identify all loops, identify completely repeated code structures, and remove all code fragments except the beginning and the end;
[0025] Step 3-2: After identifying the loop, the optimizer analyzes the loop body to see if there are consecutive identical instruction sequences with consistent operators and operands.
[0026] Step 3-3: Delete all the codes except the beginning and the end, and replace them with the final loop code.
[0027] Furthermore, in the iterative loop folding, if an exception occurs during the identification and replacement process, the loop is skipped and the original instructions are restored.
[0028] The beneficial effects of the present invention are:
[0029] Compared to existing code size optimization methods, this invention optimizes at the binary level, lowering the optimization threshold. It also leverages program performance profiling data to address the shortcomings of existing loop unrolling strategies and implement a more fine-grained optimization strategy. Compared to existing binary optimizers, this invention focuses on optimizing program size without compromising performance.
[0030] The concept, specific structure and technical effects of the present invention will be further described below in conjunction with the accompanying drawings to fully understand the purpose, characteristics and effects of the present invention. BRIEF DESCRIPTION OF THE DRAWINGS
[0031] Figure 1 This is a flow chart of the use of the binary program volume optimizer based on loop folding in the present invention.
[0032] Figure 2 It is an optimization flow chart specifically implemented by the optimizer in the present invention.
[0033] Figure 3 It is a process flow chart of loop folding for continuous memory access according to the present invention.
[0034] Figure 4 This is a screenshot of the process register code for loop folding of continuous memory accesses according to the present invention.
[0035] Figure 5 It is a flow chart of the iterative loop folding process of the present invention. DETAILED DESCRIPTION
[0036] like Figure 1 As shown, the present invention provides a binary program volume optimizer based on loop folding, the execution of which includes the following steps: Step 1, running the program and collecting performance analysis data; Step 2, inputting the binary program and performance analysis data into the optimizer; Step 3, outputting the optimized program. For an existing binary file, its performance analysis data must first be collected. Using the Linux perf tool to run the target program in a real environment and with real data, a peri.data file can be obtained, which contains the program's CPU data, instruction data, branch prediction status, and other data. This performance analysis file and the corresponding binary file are then used as input to the optimizer, which automatically analyzes and optimizes, ultimately obtaining an optimized binary file with a smaller size while maintaining similar performance.
[0037] like Figure 2 As shown, after the binary program and performance analysis data are input into the optimizer, the optimization steps of the optimizer include: step 1-1, parsing the performance analysis data; step 1-2, calculating the loop folding level of each loop; step 1-3, performing loop folding, including loop folding for continuous memory access and loop folding for iteration; step 1-4, generating an optimized executable file.
[0038] First, you need to parse the performance profile data and obtain time and instruction information about the loop code. Then calculate the folding level of each loop. From the performance profile data, you can know the number of executions and iterations of each loop. There are three levels of loop folding that can be applied to each loop. The high level completely folds the loop body to only contain one iteration of the loop. It can save the most space, but it also affects performance. The middle level cuts the expansion factor in half, and the lowest level keeps the original loop unchanged. Use the following strategy to determine the folding level corresponding to the loop, specifically:
[0039] S=0.5*time / total_loop_time+0.5*exec_num / total_exe_mum
[0040] Where S is the final folding score, time is the wall run time of the loop, total_loop_time is the total run time of all loops, exec_num is the number of times the loop is executed, and total_exe_num is the total number of times all loops are executed. If the score is less than 0.25, it is folded at the highest level. If the score is greater than 0.25 and less than 0.5, it is folded at an intermediate level. If the score is greater than 0.5, it is not folded.
[0041] Loop folding can then be performed based on the determined folding level. After loop folding, due to the degree of code reduction, gaps—in other words, blank code segments—are left in the program. To reduce the final program size, these blank code segments must be deleted. The entire program is reparsed, and two pointers are used to record blank code in the program. When blank code is identified, one pointer skips over the blank area, records the starting address of the next code segment, and adjusts the offset of the subsequent code segment to generate the final optimized program.
[0042] like Figure 3As shown, the loop folding for consecutive memory accesses described above includes the following steps: Step 2-1: disassemble the binary file, identify all loops, and find iteration registers with consecutive memory accesses; Step 2-2: schedule iteration register update instructions to update the memory addresses associated with the iteration registers; Step 2-3: calculate the loop unrolling factor and stride, followed by dependency analysis and instruction replacement. First, disassemble the binary file. The innermost loop in a loop nest is often the primary target for loop unrolling by the compiler, often consisting of only one basic block. All innermost loops within this nest are identified and filtered to check whether they have been unrolled. Since consecutive memory accesses of length 4, 8, or greater are a hallmark of loop unrolling, finding iteration registers with consecutive memory accesses is crucial for calculating the unrolling factor and stride. For each loop, the iterator register must be updated in each iteration, so an addition instruction must be present to add an immediate value to the register. If there are consecutive memory accesses, the register is checked in each immediate addition instruction. Figure 4 The register operated on by the instruction at address 0x0000001e in (a) is the iterator register. Due to instruction scheduling, this update instruction could appear anywhere within the loop body. Therefore, it should be moved to the top or bottom of the loop to facilitate the unrolling factor. I chose to move this instruction to the bottom because this is the default behavior of most compilers. Because the update instruction's location has changed, an offset must be added to all instructions that reference this register to update all associated memory addresses. I then performed a correlation analysis based on all memory offsets to the iterator register. These offsets are grouped into several groups, with the offsets in each group forming a subsequence. When all subsequences have the same length (i.e., the unrolling factor), the loop can be unrolled. If this factor is greater than 1, the loop can be folded. Next, I tried removing the unrolled portion of the loop and modifying the immediate value in the update instruction based on the desired folding level. If I simply want to fold the loop completely, I retain only the original portion and remove all other unrolled portions. Finally, I replaced the original loop with the new instruction. Since failure can occur at any stage of loop folding, I can catch exceptions and skip the loop.
[0043] like Figure 5As shown, the loop folding for iterations described above includes the following steps: Step 3-1: Disassemble the binary file, identify all loops, identify completely repetitive code structures, and remove all code fragments except the beginning and end. Step 3-2: After loop identification, the optimizer analyzes the loop body for consecutive identical instruction sequences with consistent operators and operands. Step 3-3: Delete all code except the beginning and end, replacing it with the final loop body code. For some unrolled loops without sequential memory accesses, such as iterations, these loops perform calculations only in a few registers. These cases are often difficult to identify. However, if the repetitive sections are regular, that is, they have identical code structures, the beginning and end sections are retained and the rest are removed. After loop identification, the optimizer analyzes the loop body for consecutive identical instruction sequences with consistent operators and operands. It then deletes all code except the beginning and end sections and replaces them with the final loop body code. Similarly, if an exception occurs during the identification and replacement process, the loop is skipped and the original instructions are restored.
[0044] Compared to existing code size optimization methods, this invention optimizes at the binary level, lowering the optimization threshold. It also leverages program performance profiling data to address the shortcomings of existing loop unrolling strategies and implement a more fine-grained optimization strategy. Compared to existing binary optimizers, this invention focuses on optimizing program size without compromising performance.
[0045] The above describes in detail the preferred embodiments of the present invention. It should be understood that those skilled in the art can make numerous modifications and variations based on the concepts of the present invention without inventive effort. Therefore, any technical solutions that can be derived by those skilled in the art through logical analysis, reasoning, or limited experimentation based on the concepts of the present invention and the prior art should be within the scope of protection defined by the claims.
Claims
1. A binary program size optimizer based on loop folding, characterized in that: The optimizer includes the following steps when executed: Step 1: Run the program and collect performance analysis data; Step 2: Input the binary program and performance analysis data into the optimizer. The optimizer's optimization steps include: Step 1-1: parse the performance profile data to obtain time and instruction information about the loop code; Step 1-2: Calculate the loop folding level of each loop; specifically include: Calculate the three-level loop folding level. The high-level fully folded loop body contains only one iteration of the loop. The middle level will halve the expansion factor. The lowest level keeps the original loop unchanged. The following strategy is used to determine the corresponding folding level of the loop: S=0.5*time / total_loop_time+0.5*exec_num / total_exe_num Where S is the final folding score, time is the wall running time of the loop, total_loop_time is the total running time of all loops, exec_num is the number of times the loop is executed, and total_exe_num is the total number of times all loops are executed; If the score is less than 0.25, fold it at the highest level; if the score is greater than 0.25 and less than 0.5, fold it at the middle level; if the score is greater than 0.5, do not fold it; Steps 1-3: perform loop folding, including loop folding for continuous memory access and loop folding for iteration; wherein: The loop folding for continuous memory access includes: Step 2-1: Disassemble the binary file, identify all loops, and find iterative registers with consecutive memory accesses. Step 2-2, scheduling an iteration register update instruction to update the memory address associated with the iteration register; Step 2-3: Calculate the loop expansion coefficient and step size, then perform correlation analysis and instruction replacement The loop folding of iterations includes: Step 3-1: Disassemble the binary file, identify all loops, identify completely repeated code structures, and remove all code fragments except the beginning and the end; Step 3-2: After identifying the loop, the optimizer analyzes the loop body to see if there are consecutive identical instruction sequences with consistent operators and operands. Step 3-3, delete all the codes except the beginning and end, and replace them with the final loop code; Steps 1-4: Generate optimized executable files; Step 3: Output the optimized program.
2. The binary program size optimizer based on loop folding according to claim 1, wherein: In the iterative loop folding, if an exception occurs during the identification and replacement process, the loop is skipped and the original instructions are restored.
Citation Information
Patent Citations
Method for optimizing loop folding CPU assembly line
CN106095396A