Abstract:
Methods, apparatus and computer software product for source code optimization are provided. In an exemplary embodiment, a first custom computing apparatus is used to optimize the execution of source code on a second computing apparatus. In this embodiment, the first custom computing apparatus contains a memory, a storage medium and at least one processor with at least one multi-stage execution unit. The second computing apparatus contains at least two multi-stage execution units that avow for parallel execution of tasks. The first custom computing apparatus optimizes the code for both parallelism and locality of operations on the second computing apparatus. This Abstract is provided for the sole purpose of complying with the Abstract requirement rules. This Abstract is submitted with the explicit understanding that it will not be used to interpret or to limit the scope or the meaning of the claims.

Description:
GOVERNMENT INTERESTS 
     Portions of this invention were made with U.S. Government support under SBIR contract/instrument W9113M-08-C-0146. The U.S. Government has certain rights. 
    
    
     CROSS REFERENCE TO RELATED APPLICATIONS 
     This application is related to and claims the benefit of priority to U.S. Provisional Application Ser. No. 61/097,799, entitled “STATIC SOFTWARE TOOLS TO OPTIMIZE BMD RADAR TO COTS HARDWARE”, filed Sep. 17, 2008, the entirety of which is hereby incorporated by reference. This application is additionally related to the subject matter contained in co-owned, co-pending U.S. patent application Ser. No. 12/365,780 entitled “METHODS AND APPARATUS FOR LOCAL MEMORY COMPACTION” filed Feb. 4, 2009 which claims priority to U.S. Provisional Application Ser. No. 61/065,294 both of which are additionally incorporated by reference herein in their entirety. 
     FIELD OF THE INVENTION 
     The present invention generally concerns computer programming. More particularly, the invention concerns a system, methods, and apparatus for source code compilation. 
     BACKGROUND OF THE INVENTION 
     The progression of the computer industry in recent years has illustrated the need for more complex processor architectures capable of processing large volumes of data and executing increasingly complex software. A number of systems resort to multiple processing cores on a single processor. Other systems include multiple processors in a single computing device. Additionally, many of these systems utilize multiple threads per processing core. One limitation that these architectures experience is that the current commercially available compilers can not efficiently take advantage of the increase of computational resources. 
     In the software design and implementation process, compilers are responsible for translating the abstract operational semantics of the source program into a form that makes efficient use of a highly complex heterogeneous machine. Multiple architectural phenomena occur and interact simultaneously; this requires the optimizer to combine multiple program transformations. For instance, there is often a tradeoff between exploiting parallelism and exploiting locality to reduce the ever widening disparity between memory bandwidth and the frequency of processors: the memory wall. Indeed, the speed and bandwidth of the memory subsystems have always been a bottleneck, which worsens when going to multi-core. Since optimization problems are associated with huge and unstructured search spaces, this combinational task is poorly achieved by current compilers, resulting in weak scalability and disappointing sustained performance. 
     Even when programming models are explicitly parallel (threads, data parallelism, vectors), they usually rely on advanced compiler technology to relieve the programmer from scheduling and mapping the application to computational cores, understanding the memory model and communication details. Even provided with enough static information or annotations (OpenMP directives, pointer aliasing, separate compilation assumptions), compilers have a hard time exploring the huge and unstructured search space associated with these mapping and optimization challenges. Indeed, the task of the compiler can hardly been called optimization anymore, in the traditional meaning of reducing the performance penalty entailed by the level of abstraction of a higher-level language. Together with the run-time system (whether implemented in software or hardware), the compiler is responsible for most of the combinatorial code generation decisions to map the simplified and ideal operational semantics of the source program to the highly complex and heterogeneous machine. 
     The polyhedral model promises to be a powerful framework to unify coarse grained and fine-grained parallelism extraction with locality and communication optimizations. To date, this promise has yet been unfulfilled as no existing affine scheduling and fusion techniques can perform all these optimizations in a unified (i.e., non-phase ordered) and unbiased manner. Typically, parallelism optimization algorithms optimize for degrees of parallelism, but cannot be used to optimize locality or communication. In like manner, algorithms used for locality optimization cannot be used for the extracting parallelism. Additional difficulties arise when optimizing source code for the particular architecture of a target computing apparatus. 
     Therefore there exists a need for improved source code optimization methods and apparatus that can optimize both parallelism and locality. 
     SUMMARY OF THE INVENTION 
     The present invention provides a system, apparatus and methods for overcoming some of the difficulties presented above. Various embodiments of the present invention provide a method, apparatus, and computer software product for optimization of a computer program on a first computing apparatus for execution on a second computing apparatus. 
     In an exemplary provided method computer program source code is received into a memory on a first computing apparatus. In this embodiment, the first computing apparatus&#39; processor contains at least one multi-stage execution unit. The source code contains at least one arbitrary loop nest. The provided method produces program code that is optimized for execution on a second computing apparatus. In this method the second computing apparatus contains at least two multi-stage execution units. With these units there is an opportunity for parallel operations. In its optimization of the code, the first computing apparatus takes into account the opportunity for parallel operations and locality and analyses the tradeoff of execution costs between parallel execution and serial execution on the second computing apparatus. In this embodiment, the first computing apparatus minimizes the total costs and produces code that is optimized for execution on the second computing apparatus. 
     In another embodiment, a custom computing apparatus is provided. In this embodiment, the custom computing apparatus contains a storage medium, such as a hard disk or solid state drive, a memory, such as a Random Access Memory (RAM), and at least one processor. In this embodiment, the at least one processor contains at least one multi-stage execution unit. In this embodiment, the storage medium is customized to contain a set of processor executable instructions that, when executed by the at least one processor, configure the custom computing apparatus to optimize source code for execution on a second computing apparatus. The second computing apparatus, in this embodiment, is configured with at least two multi-stage execution units. This configuration allows the execution of some tasks in parallel, across the at least two execution units and others in serial on a single execution unit. In the optimization process the at least one processor takes into account the tradeoff between the cost of parallel operations on the second computing apparatus and the cost of serial operations on a single multi-stage execution unit in the second computing apparatus. 
     In a still further embodiment of the present invention a computer software product is provided. The computer software product contains a computer readable medium, such as a CDROM or DVD medium. The computer readable medium contains a set of processor executable instructions, that when executed by a multi-stage processor within a first computing apparatus configure the first computing apparatus to optimize computer program source code for execution on a second computing apparatus. Like in the above described embodiments, the second computing apparatus contains at least two execution units. With at least two execution units there is an opportunity for parallel operations. The configuration of the first computing apparatus includes a configuration to receive computer source code in a memory on the first computing apparatus and to optimize the costs of parallel execution and serial execution of tasks within the program, when executed on the second computing apparatus. The configuration minimizes these execution costs and produces program code that is optimized for execution on the second computing apparatus. 
    
    
     
       BRIEF DESCRIPTION OF THE DRAWINGS 
       Various embodiments of the present invention taught herein are illustrated by way of example, and not by way of limitation, in the figures of the accompanying drawings, in which: 
         FIG. 1  illustrates a computer network and a computing apparatus consistent with provided embodiments; 
         FIG. 2  illustrates processors with multi-stage execution units; 
         FIG. 3  illustrates a processor with multiple multi-stage execution units; 
         FIG. 4  illustrates an embodiment of a provided method 
         FIG. 5  illustrates an embodiment of a provided method; 
         FIG. 6  illustrates an embodiment of a provided method; 
         FIG. 7  illustrates an embodiment of a provided method; 
         FIG. 8  illustrates an embodiment of a provided method; 
         FIG. 9  illustrates an embodiment of a provided method; 
         FIGS. 10(   a ) and  10 ( b ) illustrate an embodiment of a provided method; 
         FIG. 11  illustrates an embodiment of a provided method; 
         FIGS. 12(   a ) and  12 ( b ) illustrate an embodiment of a provided method; and 
         FIG. 13  illustrates an embodiment of a provided method; 
     
    
    
     It will be recognized that some or all of the Figures are schematic representations for purposes of illustration and do not necessarily depict the actual relative sizes or locations of the elements shown. The Figures are provided for the purpose of illustrating one or more embodiments of the invention with the explicit understanding that they will not be used to limit the scope or the meaning of the claims. 
     DETAILED DESCRIPTION OF THE INVENTION 
     In the following paragraphs, the present invention will be described in detail by way of example with reference to the attached drawings. While this invention is capable of embodiment in many different forms, there is shown in the drawings and will herein be described in detail specific embodiments, with the understanding that the present disclosure is to be considered as an example of the principles of the invention and not intended to limit the invention to the specific embodiments shown and described. That is, throughout this description, the embodiments and examples shown should be considered as exemplars, rather than as limitations on the present invention. Descriptions of well known components, methods and/or processing techniques are omitted so as to not unnecessarily obscure the invention. As used herein, the “present invention” refers to any one of the embodiments of the invention described herein, and any equivalents. Furthermore, reference to various feature(s) of the “present invention” throughout this document does not mean that all claimed embodiments or methods must include the referenced feature(s). 
     Embodiments of the present invention provide a custom computing apparatus, illustrated in  FIG. 1 , that is configured to optimize computer source code for operation on a second computing apparatus. As illustrated, first custom computing apparatus  10 ( a ) is configured to communicate with second computing apparatus  10 ( b ) across network  20 . A further illustration of computing apparatus  10  is provided in  FIG. 1 . In this illustration custom computing apparatus  10 ( a ) contains at least one processor  30  ( a - n ), a communication port  40  communicating with the at least one processor  30  ( a - n ). Custom computing apparatus  10 ( a ) additionally includes memory  50 , which in some embodiments includes dependence analysis module  220 . Custom computing apparatus  10 ( a ), in some embodiments, additionally includes drive  70  configured to accept external storage medium  80 . In some embodiments, external storage medium  80  is a CD, in others a DVD. In these embodiments, drive  70  is configured to accept the appropriate external storage medium  80 . While CD and DVD are specifically enumerated in these embodiments, there are many external storage media that can be used to practice various aspects of the invention therefore some embodiments are not limited to the particular drive  70  configuration or external media  80 . Custom computing apparatus  10 ( a ) additionally includes storage medium  60 . Storage medium  60  in some embodiments is a hard-disk drive, and in others is a solid state drive. In some embodiments, storage medium  60  contains a set of processor executable instructions that when executed by the at least one processor  30 ( a - n ) configure custom computing apparatus  10 ( a ) to optimize computer code for execution on computing apparatus  10 ( b ). While custom computing apparatus  10 ( a ) and computing apparatus  10 ( b ) are illustrated in  FIG. 1  communicating over network  20 , various embodiments of the invention do not require this inter-computer communication. 
     Various embodiments of the present invention are directed to processors containing multi-stage execution units, and in some embodiments multiple execution units. By way of example and not limitation to the particular multi-stage execution unit,  FIG. 2  illustrates exemplary multi-stage execution units  90 . In one embodiment, a 6-stage execution unit is utilized. In this embodiment, the stages may include instruction fetch, instruction decode, operand address generation, operand fetch, instruction execute, and result store. In another depicted multi-stage architecture, the stages include instruction fetch, instruction fetch &amp; register decode, execute, memory access and register write-back. During routine operation of a multi-stage execution unit instructions are processed sequentially moving from stage to stage. In scheduling operations on multi-stage execution unit processors there are inherent difficulties that arise. For example, one instruction in one stage of the pipeline may attempt to read from a memory location while another instruction is writing to that location. This is problem is confounded in the instance of multiple processing cores. Additionally, in multiple processor and/or multiple core architectures, the locality of data to the execution unit attempting access can create significant delays in processing. 
     A further illustration of a multiple execution unit system is depicted in  FIG. 3 . In this illustration, a first execution unit (Execution Unit  1 ) is attempting to write to a specific memory location while a second execution unit (Execution unit  2 ) is attempting to read from that same location. When both read and write occur at the same time, this causes a condition known in the art as a conflicting access which can significantly impact the speed and the correctness of execution. While it may appear that parallel execution of instructions across multiple execution units and/or processors would produce an optimal result this is not always the case. Further, as previously discussed optimization, of source code for parallelism may result in code that is poor in terms of locality or communications. In the prior approaches to code optimization, the converse is additionally true. Optimization of code for locality can result in poor parallelism and under utilization of computing resources. It is therefore an object of embodiments of the present invention to provide a customized computing apparatus, methods, and computer software product that simultaneously optimizes a computer program for execution on a particular computing device with multiple execution units. It is another object of the invention to provide embodiments of methods which can explore the complete solution space for legal schedules for potential solutions. It is a further object of the invention to provide methods containing new formulations that encode the tradeoffs between locality and parallelism directly in the constraints and the objective functions of an optimization problem. 
     The following code example illustrates loop fusion. Given the following code: 
                                                                                     int i, a[100], b[100];           for (i = 0; i &lt; 100; i++) {                a[i] = 1;                }           for (i = 0; i &lt; 100; i++) {                b[i] = 2;                }                        
The effect of loop fusion is to interleave the execution of the first loop with the execution of the second loop.
 
                                                                 int i, a[100], b[100];           for (i = 0; i &lt; 100; i++) {                a[i] = 1;           b[i] = 2;                }                        
A consequence of loop fusion is that memory locations a[i] and b[i] referenced by the former 2 loops are now accessed in an interleaved fashion. In the former code, memory locations were accessed in the order a[0], a[1], . . . a[100] then b[0], b[1], . . . b[100]. In the code comprising the fused loops, the memory locations are now accessed in the order a[0], b[0], a[1], b[1], . . . a[100], b[100]. Loop fusion can lead to better locality when multiple loops access the same memory locations. It is common general knowledge in the field of compilers that better locality reduces the time a processing element must wait for the data resident in memory to be brought into a local memory such as a cache or a register. In the remainder of this document, we shall say that loops are fused or equivalently that they are executed together when such a loop fusion transformation is applied to the received program to produce the optimized program.
 
     Loop fusion can change the order in which memory locations of a program are accessed and require special care to preserve original program semantics: 
                                                                                     int i, a[100], b[100];           for (i = 0; i &lt; 100; i++) {                a[i] = 1;                }           for (i = 0; i &lt; 100; i++) {                b[i] = 2 + a[i+1];                }                        
In the previous program, the computation of b[i] depends on the previously computed value of a[i+1]. Simple loop fusion in that case is illegal. If we consider the value computed for b[0]=2+a[1], in the following fused program, b[0] will read a[1] at iteration i=0, before a[1] is computed at iteration i=1.
 
                                                                 int i, a[100], b[100];           for (i = 0; i &lt; 100; i++) {                a[i] = 1;           b[i] = 2 + a[i+1];                }                        
It is common general knowledge in the field of high-level compiler transformations that enabling transformations such as loop shifting, loop peeling, loop interchange, loop reversal, loop scaling and loop skewing can be used to make fusion legal.
 
     The problem of parallelism extraction is related to the problem of loop fusion in the aspect of preserving original program semantics. A loop in a program can be executed in parallel if there are no dependences between its iterations. For example, the first program loop below can be executed in parallel, while the second loop must be executed in sequential order: 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 int i, a[100], b[100]; 
               
               
                   
                 for (i = 0; i &lt; 100; i++) { 
               
             
          
           
               
                   
                 a[i] = 1; 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (i = 1; i &lt; 100; i++) { 
               
             
          
           
               
                   
                 b[i] = 2 + b[i−1]; 
               
             
          
           
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
     It is common knowledge in the field of high-level compiler transformations that the problems of fusion and parallelism heavily influence each other. In some cases, fusing 2 loops can force them to be executed sequentially. 
     Loop permutability is another important property of program optimizations. A set of nested loop is said permutable, if their order in the loop nest can be interchanged without altering the semantics of the program. It is common knowledge in the field of high-level compiler optimization that loop permutability also means the loops in the permutable set of loops dismiss the same set of dependences. It is also common knowledge that such dependences are forward only when the loops are permutable. This means the multi-dimensional vector of the dependence distances has only non-negative components. Consider the following set of loops: 
                                                                                     int i,j, a[100][100], b[100][100];           for (i = 0; i &lt; 99; i++) {                for (j = 0; j &lt; 99; j++) {                a[i+1][j+1] = a[i][j] + a[i][j+1]; // statement S                }                }                        
There are 2 flow dependences between the statement S and itself. The two-dimensional dependence vectors are: (i−(i−1), j−(j−1))=(1,1) and (i−(i−1), j−j)=(1, 0). The components of these vectors are nonnegative for all possible values of i and j. Therefore the loops I and j are permutable and the loop interchange transformation preserves the semantics of the program. If loop interchange is applied, the resulting program is:
 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 int i,j, a[100][100], b[100][100]; 
               
               
                   
                 for (j = 0; j &lt; 99; j++) { 
               
             
          
           
               
                   
                 for (i = 0; i &lt; 99; i++) { 
               
             
          
           
               
                   
                 a[i+1][j+1] = a[i][j] + a[i][j+1]; // statement S 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
     Loop permutability is important because it allows loop tiling (alternatively named loop blocking). Loop tiling is a transformation that changes the order of the iterations in the program and ensures all the iterations of a tile are executed before any iteration of the next tile. When tiling by sizes (i=2, j=4) is applied to the previous code, the result is: 
                                                                                                             int i,j,ii,jj a[100][100], b[100][100];       for (j = 0; j &lt; 99; j+=4) {                for (i = 0; i &lt; 99; i+=2) {                for (jj = 4*j; jj &lt; 4*j+4; jj++) {                for (ii = 2*i; ii &lt; 2*i+2; ii++) {                a[ii+1][jj+1] = a[ii][jj] + a[ii][jj+1]; // statement S                }                }                }            }                    
Consider the memory locations written by the statement S. Before tiling, the locations are written in this order: a[1][1], a[1][2] . . . a[1][99], a[2][1], a[2][2] . . . a[2][99], a[3][1] . . . . After tiling, the new order of writes is the following: a[1][1], a[2][1], a[1][2], a[2][2] . . . a[1][4], a[2][4], a[4][1], a[5][1], a[4][2], a[5][2] . . . a[4][4], a[5][4]. . . . It is additionally common knowledge that loop tiling results in better locality when the same memory locations are written and read multiple times during the execution of a tile.
 
     Loop tiling is traditionally performed with respect to tiling hyperplanes. In this example, the tiling hyperplanes used are the trivial (i) and (j) hyperplanes. In the general case, any linearly independent combination of hyperplanes may be used for tiling, provided it does not violate program semantics. For example, (i+j) and (i+2*j) could as well be used and the resulting program would be much more complex. 
     Another important loop transformation is loop skewing. It is common knowledge that loop permutability combined with loop skewing results in the production of parallelism. In the following permutable loops, the inner loop can be executed in parallel after loop skewing: 
                                                                                     int i,j a[100][100], b[100][100];           for (i = 0; i &lt; 100; i++) {                for (j = 0; j &lt; 100; j++) {                a[i+1][j+1] = a[i][j] + a[i][j+1];                }                }                        
After loop skewing the code is the following and the inner loop j is marked for parallel execution:
 
                                                                                     int i,j a[100][100], b[100][100];           for (i = 0; i &lt; 197; i++) {                doall (j = max(0, i−98); j &lt;= min(98,i); j++) {                a[i+1−j][j+1] = a[i−j][j] + a[i−j][j+1];                }                }                        
The skewing transformation helps extract parallelism at the inner level when the loops are permutable. It is also common knowledge that loop tiling and loop skewing can be combined to form parallel tiles that increase the amount of parallelism and decrease the frequency of synchronizations and communications in the program.
 
     The problem of jointly optimizing parallelism and locality by means of loop fusion, parallelism, loop permutability, loop tiling and loop skewing is a non-trivial tradeoff. It is one of the further objects of this invention to jointly optimize this tradeoff. 
     When considering high-level loop transformations, it is common practice to represent dependences in the form of affine relations. The first step is to assign to each statement in the program an iteration space and an iteration vector. Consider the program composed of the 2 loops below: 
                                                                                     for (i = 1; i &lt;= n; i++) {                for (j = 1 ; j &lt;= n; j++) {                a[i][j] = a[i][−1 + j] + a[j][i]; // statement S                }                }                        
The iteration domain of the statement S is D={[i, j] in Z2|1≦i≦n, 1≦j≦n}. The second step is to identify when two operations may be executed in parallel or when a producer consumer relationship prevents parallelism. This is done by identifying the set of dependences in the program. In this example, the set of dependences is: R={[[i, j], [i′, j′]]|i=i′, j=j′−1, [i, j] in D, [i′, j′] in D, &lt;S, [i, j]&gt;&lt;&lt;&lt;S, [i′, j′]&gt;} union {[[i, j], [i′, j′]]|i=j′, i=j′, [i, j] in D, [i′, j′] in D, &lt;S, [i, j]&gt;&lt;&lt;&lt;S, [i′, j′]&gt;}, where &lt;&lt; denoted multi-dimensional lexicographic ordering. This relationship can be rewritten as: a[i,j] a[j,i] {([i, j], [j, i])|1≦j, i≦n, −j+i−1≧0} union a[i,j] a[i,j−1] {([i, j+1], [i, j])|1≦j≦n−1, 0≦i≦n}.
 
     It is common practice to represent the dependence relations using a directed dependence graph, whose nodes represent the statements in the program and whose edges represent the dependence relations. In the previous example, the dependence graph has 1 node and 2 edges. It is common practice to decompose the dependence graph in strongly connected components. Usually, strongly connected components represent loops whose semantics require them to be fused in the optimized code. There are many possible cases however and one of the objects of this invention is also to perform the selective tradeoff of which loops to fuse at which depth. It is common knowledge that a strongly connected component of a graph is a maximal set of nodes that can be reached from any node of the set when following the directed edges in the graph. 
     One-Dimensional Affine Fusion 
     One embodiment incorporates fusion objectives into affine scheduling constraints. Affine fusion, as used herein means not just merging two adjacent loop bodies together into the same loop nests, but also include loop shifting, loop scaling, loop reversal, loop interchange and loop skewing transformations. In the α/β/γ convention this means that we would like to have the ability to modify the linear part of the schedule, α, instead of just β and γ. Previous fusion works are mostly concerned with adjusting the β component (fusion only) and sometimes both the β and γ components (fusion with loop shifting). One embodiment of the invention, computes a scheduling function used to assign a partial execution order between the iterations of the operations of the optimized program and to produce the resulting optimized code respecting this partial order. 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
               
               
                 Fusion example. 
               
               
                   
               
             
             
               
                   
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= N; i++) { 
               
             
          
           
               
                   
                 for (int j = 0; j &lt;= M; j++) { 
               
             
          
           
               
                   
                 A[i][j] = f(C[−2 + i][1 + j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = 0; j &lt;= M; j++) { 
               
             
          
           
               
                   
                 B[i][j] = g(A[i][1 + j], A[i][j], C[−1 + i][j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = 0; j &lt;= M; j++) { 
               
             
          
           
               
                   
                 C[i][j] = h(B[i][j], A[i][2 + j], A[i][1 + j]); 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
     As a simple motivational example demonstrating the power of affine fusion, consider the example above. Dependencies between the loop nests prevents the loops from being fused directly, unless loop shifting is used to peel extra iterations of the first and second loops. The resulting transformation is shown below. 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
               
               
                 Result of fusion by shifting. 
               
               
                   
               
             
             
               
                   
               
             
          
           
               
                   
                 if (M &gt;= 0) { 
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= N; i++) { 
               
             
          
           
               
                   
                 for (int j = −2; j &lt;= min(M + −2, −1); j++) { 
               
             
          
           
               
                   
                 A[i][2 + j] = f(C[−2 + i][3 + j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = 0; j &lt;= M + −2; j++) { 
               
             
          
           
               
                   
                 A[i][2 + j] = f (C[−2 + i][3 + j]); 
               
               
                   
                 B[i][j] = g(A[i][1 + j], A[i][j], C[−1 + i][j]); 
               
               
                   
                 C[i][j] = h(B[i][j], A[i][2 + j], A[i][1 + j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = max(0, M + −1); j &lt;= M; j++) { 
               
             
          
           
               
                   
                 B[i][j] = g(A[i][1 + j], A[i][j], C[−1 + i][j]); 
               
               
                   
                 C[i][j] = h(B[i][j], A[i][2 + j], A[i][1 + j]); 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
     On the other hand, affine fusion gives a superior transformation, as shown above. In this transformation, the fusion-preventing dependencies between the loop nests are broken with a loop reversal rather than loop shifting, and as a result, no prologue and epilogue code is required. Furthermore, the two resulting loop nests are permutable. Thus we can further apply tiling and extract one degree of parallelism out of the resulting loop nests. 
     
       
         
               
             
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
             
           
               
                   
               
               
                 Result of affine fusion. 
               
               
                   
               
             
             
               
                   
               
             
          
           
               
                 if (M &gt;= 0) { 
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= N; i++) /* perm=0 */ { 
               
             
          
           
               
                   
                 for (int j = − M; j &lt;= 0; j++) /* perm=0 */ { 
               
             
          
           
               
                   
                 A[i][ − j] = f(C[−2 + i][1 − j]); 
               
               
                   
                 B[i][ − j] = g(A[i][1 − j], A[i][ − j], C[−1 + i][ − j]); 
               
               
                   
                 C[i][ − j] = h(B[i][ − j], A[i][2 − j], A[i][1 − j]); 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                 } 
               
               
                   
               
             
          
         
       
     
     Many prior art algorithms cannot find this transformation with their restrictions. Some of the restrictions prune out the solution space based on loop reversals, and thus these algorithms can only find the loop-shifting based solutions. Another important criteria is that fusion should not be too greedy, i.e., aggressive fusion that destroys parallelism should be avoided. On the other hand, fusion that can substantially improve locality may sometimes be preferred over an extra degree of parallelism, if we already have obtained sufficient degrees of parallelism to fill the hardware resources. For instance, consider the combined matrix multiply example. This transformation is aggressive, and it gives up an additional level of synchronization-free parallelism that may be important on some highly parallel architectures. It is a further object of this invention to properly model the tradeoff between benefits of locality and parallelism for different hardware configurations. 
     The code below shows the result of applying fusion that does not destroy parallelism. The two inner i-loops are fissioned in this transformation, allowing a second level of synchronization-free parallelism. 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
               
               
                 Partially fusing two matrix multiplies. 
               
               
                   
               
             
             
               
                   
               
             
          
           
               
                   
                 doall (int i = 0; i &lt;= n + −i; i++) { 
               
             
          
           
               
                   
                 doall (int j = 0; j &lt;= n + −1; j++) { 
               
             
          
           
               
                   
                 C[j][i] = 0; 
               
               
                   
                 for (int k = 0; k &lt;= n + −1; k++) { 
               
             
          
           
               
                   
                 C[j][i] = C[j][i] + A[j][k] * B[k][i]; 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
               
                   
                 doall (int j = 0; j &lt;= n + −1; j++) { 
               
             
          
           
               
                   
                 for (int k = 0; k &lt;= n + −1; k++) { 
               
             
          
           
               
                   
                 D[j][i] = D[j][i] + C[k][i] * E[j][i]; 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
             
          
           
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
     Affine Fusion Formulation 
     The tension between fusion and scheduling implies that fusion and scheduling should be solved in a unified manner. For any loop p, we compute a cost ω p  which measures the slowdown in execution if the loop is executed sequentially rather than in parallel. Similarly, for each pair of loop nests (p, q), we estimate upq the cost in performance if the two loops p and q remains unfused. The cost ω p  can be interpreted to be the difference between sequential and parallel execution times, and the cost upq can be interpreted as the savings due to cache or communication based locality. In one embodiment, the cost ω p  is related to a difference in execution speed between sequential operations of the at least one loop on a single execution unit in the second computing apparatus and parallel operations of the at least one loop on more than one of the at least two execution units in the second computing apparatus. In another embodiment, the cost upq is related to a difference in execution speed between operations where the pair of loops are executed together on the second computing apparatus, and where the pair of loops are not executed together on the second computing apparatus. 
     In an illustrative example, let the Boolean variable Δ p  denote whether the loop p is executed in sequence, and let the variable fpq denote whether the two loops p and q remain unfused, i.e. Δ p =0 means that p is executed in parallel, and fpq=0 means that edge loops p and q have been fused. Then by minimizing the weighted sum 
                 ∑   p     ⁢           ⁢       w   p     ⁢     Δ   p         +       ∑     p   ,   q       ⁢           ⁢       u   pq     ⁢     f   pq               
we can optimize the total execution cost pertaining to fusion and parallelism. In some embodiment, the variable Δ p  specifies if the loop is executed in parallel in the optimized program. In another embodiment, the variable f pq  specifies if the pair of loops are executed together in the optimized program.
 
     In some embodiment, the value of the cost w p  is determined by a static evaluation of a model of the execution cost of the instructions in the loop. In another embodiment, the value of the cost w p  is determined through the cost of a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop. In a further embodiment, the value of the cost w p  is determined by an iterative process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop. 
     In some embodiment, the value of the cost u pq  is determined by a static evaluation of a model of the execution cost of the instructions in the loop pair. In another embodiment, the value of the cost u pq  is determined through the cost of a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop pair. In a further embodiment, the value of the cost u pq  is determined by an iterative process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop pair. 
     The optimization can be formulated as follows. In one embodiment, we divide up the generalized dependence graph, GDG G=(V, E) into strongly connected components (SCCs) and consider each SCC to be a separate fusible “loop” candidate. Let G′=(V′, E′) denote the SCC induced subgraph where V′ denotes the SCCs and E′ the edges between SCCs. Given a node v ε V, let sec(v) denote the component in which v belongs to in the SCC decomposition. Given (p, q) E E′, let the Boolean variables f pq  denote whether two SCCs has been fused, i.e., f pa =0 denotes that the loops corresponding to p and q have been fused.
 
 f   pq ε{0,1},  (5)
 
( p,q )ε E′   (6)
 
     There are multiple possible strategies to encode the restrictions implied by E′. In one embodiment, we directly encode the transitivity relation E′ as constraints, i.e. (i) given edges (p,q) and (q,r) and (p,q′), if loops (p,q) or (q,1′) is not fused then (p,r) cannot be fused, and (ii) if (p, q) and (q, r) are fused then (p, q) must be fused:
 
 f   pq   ,f   qr   ≦f   pr , ( p,q ),( q,r ),( p,r )ε E′   (7)
 
 f   pq   +f   qr   ≧f   pr , ( p,q ),( q,r ),( p,r )ε E′   (8)
 
     One potential deficiency of this strategy is that up to O(|V′| 3  constraints are required. In the second embodiment we adopt, involves the encoding of the β schedule coordinates directly in the constraints. In this encoding, β p =β q  implies that loops p and q have been fused:
 
β p ε{0,| V′|− 1}  pεV′   (9)
 
β p ≧β q   +f   pq  ( p,q )ε E′   (10)
 
β q −β p   ≧−|V′|f   pq , ( p,q )ε E′   (11)
 
     Given the constraints on f pq  in place, we can now provide a suitable modification to the schedule constraints. The constraints are divided into two types, the first involves edges within the same SCC, and the second involves edges crossing different SCCs: 
                         δ   p     ⁡     (   y   )       ≥         ϕ     s   ⁡     (   e   )         ⁡     (     j   ,   y     )       -       ϕ     t   ⁡     (   e   )         ⁡     (     i   ,   j     )         ≥   0     ,     
     ⁢               (     i   ,   j     )     ∈       R   e     ⁡     (   y   )         ,                 p   =     scc   ⁡     (     s   ⁡     (   e   )       )         ,               q   =     scc   ⁡     (     t   ⁡     (   e   )       )                   p   =   q                   (   12   )                     δ   pq     ⁡     (   y   )       ≥         ϕ     s   ⁡     (   e   )         ⁡     (     j   ,   y     )       -       ϕ     t   ⁡     (   e   )         ⁡     (     i   ,   y     )         ≥       -     N   ∞       ⁢       F   pq     ⁡     (   y   )           ,     
     ⁢               (     i   ,   j     )     ∈       R   e     ⁡     (   y   )         ,                 p   =     scc   ⁡     (     s   ⁡     (   e   )       )         ,                 q   =     scc   ⁡     (     t   ⁡     (   e   )       )         ,                 p   ≠   q     ⁢                           (   13   )                 F   pq ( y )= f   pq ( yl+yk+ 1)  (14)
 
     Here, the term −N ∞ F pq (y) is defined in such a way that −N ∞ F pq (y)=0 when f pq =0, and is equal to a sufficiently large negative function when f pq =1. Thus, φ s(e) (j,y)−φ t(e) (i,y)≧0 only needs to hold only if the edge e has been fused or is a loop-carried edge. The final set of constraints is to enforce the restriction that δ P (y)=δ q (y) if (p, q) has been fused. The constraints encoding this are as follows:
 
δ p ( y )−δ q ( y )+ N   ∞   F   pq ( y )≧0 ( p,q )εE′  (15)
 
δ q ( y )−δ p ( y )+ N   ∞   F   pq ( y )≧0 ( p,q )εE′  (16)
 
δ pq ( y )−δ p ( y )+ N   ∞   F   pq ( y )≧0 ( p,q )εE′  (17)
 
     Some embodiments additionally specify that a schedule dimension at a given depth must be linearly independent from all schedule dimensions already computed. Such an embodiment computes the linear algebraic kernel of the schedule dimensions found so far. In such an embodiment, for a given statement S, h denotes the linear part of φS, the set of schedule dimensions already found and J denotes a subspace linearly independent of h. A further embodiment derives a set of linear independence constraints that represent the additional Jh≠0 and does not restrict the search to Jh&gt;0. Such linear independence constraints may be used to ensure successive schedule dimensions are linearly independent. In particular, such an embodiment, that does not restrict the search to Jh&gt;0, exhibits an optimization process that can reach any legal multidimensional affine scheduling of the received program including combinations of loop reversal. 
     In some embodiments the set of conditions preserving semantics is the union of all the constraints of the form φ s(e) (j,y)−φ t(e) (i,y)≧0. In another embodiment, the optimizing search space that encompasses all opportunities in parallelism and locality is the conjunction of all the constraints (5)-(17). 
     In further embodiments, the set of affine constraints (12) and (13) is linearized using the affine form of Farkas lemma and is based on at least one strongly connected component of the generalized dependence graph. 
     In other embodiments, the constraints of the form (12) are used to enforce dimensions of schedules of loops belonging to the same strongly connected component are permutable. 
     In further embodiments, the constraints of the form (13) are used to ensure that dimensions of schedules of loops that are not executed together in the optimized program do not influence each other. In such embodiments, the constraints of the form (13) use a large enough constant to ensure that dimensions of schedules of loops that are not executed together in the optimized program do not influence each other. 
     In some embodiments, the linear weighted sum 
                 ∑   p     ⁢           ⁢       w   p     ⁢     Δ   p         +       ∑     p   ,   q       ⁢           ⁢       u   pq     ⁢     f   pq               
can be optimized directly with the use of an integer linear programming mathematical solver such as Cplex. In other embodiments, a non-linear optimization function such as a convex function may be optimized with the use of a convex solver such as CSDP. Further embodiments may devise non-continuous optimization functions that may be optimized with a parallel satisfiability solver.
 
     Boolean Δ Formulation 
     The embodiments described so far depend on a term (or multiple terms) δ(y) which bounds the maximal dependence distance. Another embodiment may opt for the following simpler formulation. First, we assign each SCC p in the GDG a Boolean variable Δ p  where Δ p =0 means a dependence distance of zero (i.e., parallel), and 
     Δ p =1 means some non-zero dependence distance:
 
Δ p ε{0,1}  pεV′   (18)
 
Define the functions Δp(y) and Δpq(y) as:
 
Δ p ( y )=Δ p ×( y 1+ . . . + yk+ 1)  (19)
 
Δ pq ( y )=Δ pq ×( y 1+ . . . + yk+ 1)  (20)
 
Then the affine fusion constraints can be rephrased as follows:
 
     
       
         
           
             
               
                 
                   
                     
                       
                         N 
                         ∞ 
                       
                       ⁢ 
                       
                         
                           Δ 
                           p 
                         
                         ⁡ 
                         
                           ( 
                           y 
                           ) 
                         
                       
                     
                     ≥ 
                     
                       
                         
                           ϕ 
                           
                             s 
                             ⁡ 
                             
                               ( 
                               e 
                               ) 
                             
                           
                         
                         ⁡ 
                         
                           ( 
                           
                             j 
                             , 
                             y 
                           
                           ) 
                         
                       
                       - 
                       
                         
                           ϕ 
                           
                             t 
                             ⁡ 
                             
                               ( 
                               e 
                               ) 
                             
                           
                         
                         ⁡ 
                         
                           ( 
                           
                             i 
                             , 
                             y 
                           
                           ) 
                         
                       
                     
                     ≥ 
                     0 
                   
                   , 
                   
                     
 
                   
                   ⁢ 
                   
                     
                       
                         
                           
                             
                               ( 
                               
                                 i 
                                 , 
                                 j 
                               
                               ) 
                             
                             ∈ 
                             
                               
                                 R 
                                 e 
                               
                               ⁡ 
                               
                                 ( 
                                 y 
                                 ) 
                               
                             
                           
                           , 
                         
                       
                     
                     
                       
                         
                           
                             p 
                             = 
                             
                               scc 
                               ⁡ 
                               
                                 ( 
                                 
                                   s 
                                   ⁡ 
                                   
                                     ( 
                                     e 
                                     ) 
                                   
                                 
                                 ) 
                               
                             
                           
                           , 
                         
                       
                     
                     
                       
                         
                           q 
                           = 
                           
                             scc 
                             ⁡ 
                             
                               ( 
                               
                                 t 
                                 ⁡ 
                                 
                                   ( 
                                   e 
                                   ) 
                                 
                               
                               ) 
                             
                           
                         
                       
                     
                     
                       
                         
                           
                             
                               if 
                               ⁢ 
                               
                                   
                               
                               ⁢ 
                               p 
                             
                             = 
                             q 
                           
                           ⁢ 
                           
                               
                           
                         
                       
                     
                   
                 
               
               
                 
                   ( 
                   21 
                   ) 
                 
               
             
             
               
                 
                   
                     
                       N 
                       ∞ 
                     
                     ⁢ 
                     
                       
                         Δ 
                         pq 
                       
                       ⁡ 
                       
                         ( 
                         y 
                         ) 
                       
                     
                   
                   ≥ 
                   
                     
                       
                         ϕ 
                         
                           s 
                           ⁡ 
                           
                             ( 
                             e 
                             ) 
                           
                         
                       
                       ⁡ 
                       
                         ( 
                         
                           j 
                           , 
                           y 
                         
                         ) 
                       
                     
                     - 
                     
                       
                         ϕ 
                         
                           t 
                           ⁡ 
                           
                             ( 
                             e 
                             ) 
                           
                         
                       
                       ( 
                       
                         i 
                         , 
                         
                           y 
                           ( 
                           
                             
                               ≥ 
                               
                                 
                                   - 
                                   
                                     N 
                                     ∞ 
                                   
                                 
                                 ⁢ 
                                 
                                   
                                     F 
                                     pq 
                                   
                                   ⁡ 
                                   
                                     ( 
                                     y 
                                     ) 
                                   
                                 
                               
                             
                             , 
                             
                               
 
                             
                             ⁢ 
                             
                               
                                 
                                   
                                     
                                       
                                         ( 
                                         
                                           i 
                                           , 
                                           j 
                                         
                                         ) 
                                       
                                       ∈ 
                                       
                                         
                                           R 
                                           e 
                                         
                                         ⁡ 
                                         
                                           ( 
                                           y 
                                           ) 
                                         
                                       
                                     
                                     , 
                                   
                                 
                               
                               
                                 
                                   
                                     
                                       p 
                                       = 
                                       
                                         scc 
                                         ⁡ 
                                         
                                           ( 
                                           
                                             s 
                                             ⁡ 
                                             
                                               ( 
                                               e 
                                               ) 
                                             
                                           
                                           ) 
                                         
                                       
                                     
                                     , 
                                   
                                 
                               
                               
                                 
                                   
                                     
                                       q 
                                       = 
                                       
                                         scc 
                                         ⁡ 
                                         
                                           ( 
                                           
                                             t 
                                             ⁡ 
                                             
                                               ( 
                                               e 
                                               ) 
                                             
                                           
                                           ) 
                                         
                                       
                                     
                                     , 
                                   
                                 
                               
                               
                                 
                                   
                                     
                                       
                                         if 
                                         ⁢ 
                                         
                                             
                                         
                                         ⁢ 
                                         p 
                                       
                                       ≠ 
                                       q 
                                     
                                     ⁢ 
                                     
                                         
                                     
                                   
                                 
                               
                             
                           
                         
                       
                     
                   
                 
               
               
                 
                   ( 
                   22 
                   ) 
                 
               
             
             
               
                 
                   
                     
                       F 
                       pq 
                     
                     ⁡ 
                     
                       ( 
                       y 
                       ) 
                     
                   
                   = 
                   
                     
                       f 
                       pq 
                     
                     ⁡ 
                     
                       ( 
                       
                         
                           y 
                           ⁢ 
                           
                               
                           
                           ⁢ 
                           1 
                         
                         + 
                         … 
                         + 
                         yk 
                         + 
                         1 
                       
                       ) 
                     
                   
                 
               
               
                 
                   ( 
                   23 
                   ) 
                 
               
             
             
               
                 
                   
                     
                       
                         
                           
                             Δ 
                             p 
                           
                           - 
                           
                             Δ 
                             q 
                           
                           + 
                           
                             f 
                             pq 
                           
                         
                         ≥ 
                         0 
                       
                     
                     
                       
                         
                           ( 
                           
                             p 
                             , 
                             q 
                           
                           ) 
                         
                         ∈ 
                         
                           E 
                           ′ 
                         
                       
                     
                   
                 
               
               
                 
                   ( 
                   24 
                   ) 
                 
               
             
             
               
                 
                   
                     
                       
                         
                           
                             Δ 
                             q 
                           
                           - 
                           
                             Δ 
                             p 
                           
                           + 
                           
                             f 
                             pq 
                           
                         
                         ≥ 
                         0 
                       
                     
                     
                       
                         
                           ( 
                           
                             p 
                             , 
                             q 
                           
                           ) 
                         
                         ∈ 
                         
                           E 
                           ′ 
                         
                       
                     
                   
                 
               
               
                 
                   ( 
                   25 
                   ) 
                 
               
             
             
               
                 
                   
                     
                       
                         
                           
                             Δ 
                             pq 
                           
                           - 
                           
                             Δ 
                             p 
                           
                           + 
                           
                             f 
                             pq 
                           
                         
                         ≥ 
                         0 
                       
                     
                     
                       
                         
                           ( 
                           
                             p 
                             , 
                             q 
                           
                           ) 
                         
                         ∈ 
                         
                           E 
                           ′ 
                         
                       
                     
                   
                 
               
               
                 
                   ( 
                   26 
                   ) 
                 
               
             
           
         
       
     
     Multi-Dimensional Affine Fusion 
     Affine fusion formulation is a depth by depth optimization embodiment. A further embodiment described in  FIGS. 10(   a ),  10 ( b ) and  11  shows a method to derive scheduling functions for a given hardware parallelism and memory hierarchy. A further embodiment described in  FIG. 13  shows a method to derive scheduling functions for multiple levels of hardware parallelism and memory hierarchies, more specifically by formulating and optimizing at least one global weighted parametric function for each level of the parallelism and memory hierarchy of the second computing apparatus. In a further embodiment, it is a further object of the invention to build a single multi-dimensional affine fusion formulation as an alternative or as a supplement to the depth-by-depth affine fusion formulation. The single multi-dimensional fusion formulation relies on a single multi-dimensional convex affine space. More specifically, an embodiment of such a single multi-dimensional convex affine space assigns variables and relations for loops, loops pairs and dependence edges e at each scheduling dimension k. 
     The variables and their interpretations are:
         δ e   k (y)—the maximal dependence distance for edge e in dimension k.   δ k           y         —the maximal dependence distance for the loop in which statement a resides, in dimension k. If L is a loop (SCC) in dimension k then for all statements a, b, εl, δ a   k (y)=δ b   k (y).   β k     a   —the strongly connected component index (loop number) in which statement a appears.   φ a   k (i)—schedule of statement a in dimension k.   ε e   k —equal to 1 if the schedule at dimension k strictly satisfy e, i.e., φ s(e)   k (i,y)−φ t(e)   k (j,y)≧1,e εE.   p e   k —a Boolean variable, 0 only if ε e   k-1 =ε e   k =1.   p a   k —a Boolean variable, 0 only if the schedules in dimensions k−1 and k are permutable in the loop in which a resides. If a and b belongs to the same loop in dimension k, then p a   k =p b   k .       

                               δ   e   k     ⁡     (   y   )       ≥         ϕ     s   ⁡     (   e   )       k     ⁡     (     i   ,   y     )       -       ϕ     t   ⁡     (   e   )       k     ⁡     (     j   ,   y     )         ≥     ⁢     ∈   e   k     ⁢     -       N   ∞     (       ∑       k   ′     &lt;   k       ⁢           ⁢     ∈   e     k   ′         )                 (     i   ,   j     )     ∈       R   e     ⁡     (   y   )                     (   27   )                       ∈   e   k     ∈     {     0   ,   1     }             e   ⁢           ∈   E                 (   28   )                       ∈   e   e     ∈     {   0   }             e   ∈   E                 (   29   )               
The following constraints ensure that p e   k =0 only if ε e   k-1 =1 and ε e   k =1:
 
 p   e   k ε{0,1}  eεE   (30)
 
ε e   k-1 ε e   k +2 p   e   k ≧2,  eεE   (31)
 
The next constraints encode the β component of the schedules.
 
                         β   e   k           ∈     {     0   ,          V        -   1       }                   (   32   )                         β     s   ⁡     (   e   )       k     -     β     t   ⁡     (   e   )       k       ≥     -       N   ∞     (       ∑       k   ′     &lt;   k       ⁢           ⁢     ∈   e     k   ′         )               e   ∈   E                 (   33   )               
The next set of constraints ensures that all δ a   k (y) terms are the same for all nodes a which belong to the same loop nest:
 
δ s ( e ) k ( y )−δ e   k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (34)
 
δ e   k ( y )−δ s ( e ) k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (35)
 
δ t ( e ) k ( y )−δ e   k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (36)
 
δ e   k ( y )−δ t ( e ) k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (37)
 
δ s ( e ) k ( y )−δ t ( e ) k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (38)
 
δ t ( e ) k ( y )−δ s ( e ) k ( y )≦ N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (39)
 
Similarly, the next set of constraints ensure that all p a   k  are identical for all nodes a which belong in the same loop nest.
 
 p   s ( e ) k   −p   e   k   ≦N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (40)
 
 p   s   k   −p   t ( e ) k   ≦N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (41)
 
 p   s ( e ) k   −p   t ( e ) k   ≦N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (42)
 
 p   t ( e ) k   −p   s ( e ) k   ≦N   ∞ (β s ( e ) k −β t ( e ) k )  eεE   (43)
 
     In some embodiment, the strong satisfaction variable E_{k,e} assigned to each schedule dimension k and each edge e of the at least one strongly connected component is ε e   k  which is equal to 1 when the schedule difference at dimension k strictly satisfies edge e (i.e. when φ s(e)   k (i,y)−φ t(e)   k (j,y)≧1,e εE), 0 otherwise. In other embodiments, the loop permutability Boolean variable p_{k,e} assigned to each schedule dimension and each edge e of the at least one strongly connected component is p e   k . 
     In a further embodiment the statement permutability Boolean variable p_{k,a} assigned to each schedule dimension and each statement a of the at least one strongly connected component is p a   k . In another embodiment, constraints of the form (27), (28) and (29) are added to ensure dimensions of schedules of statements linked by a dependence edge in the generalized dependence graph do not influence each other at depth k if the dependence has been strongly satisfied up to depth k−1. In a further embodiment, constraints of the form (30) and (31) are added to link the strong satisfiability variables to the corresponding loop permutability Boolean variables. In another embodiment, constraints of the form (34) to (43) are added to ensure statement permutability Boolean variables are equal for all the statements in the same loop nest in the optimized program. In a further embodiment, the conjunction of the previous constraints forms a single multi-dimensional convex affine search space of all legal multi-dimensional schedules that can be traversed exhaustively or using a speeding heuristic to search for schedules to optimize any global cost function. 
     One example of an embodiment tailored for successive parallelism and locality optimizations is provided for an architecture with coarse grained parallel processors, each of them featuring fine grained parallel execution units such as SIMD vectors. One such architecture is the Intel Pentium E 5300. The following example illustrates how an embodiment of the invention computes schedules used to devise multi-level tiling hyperplanes and how a further embodiment of the invention may compute different schedules for different levels of the parallelism and memory hierarchy of the second computing apparatus. Consider the following code representing a 3-dimensional Jacobi iteration stencil. In a first loop, the array elements A[i][j][k] are computed by a weighted sum of the 7 elements, B[i][j][k], B[i−1][j][k], B[i+1][j][k], B[i][j−1][k], B[i][j+1][k], B[i][j][k−1] and B[i][j][k+1]. In a symmetrical second loop, the array elements B[i][j][k] are computed by a weighted sum of 7 elements of A. The computation is iterated Titer times. 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
               
             
             
               
                 for (t=0; t&lt;Titer; t++) { 
               
             
          
           
               
                   
                 for (i=1; i&lt;N−1; i++) { 
               
             
          
           
               
                   
                 for (j=1; j&lt;N−1; j++) { 
               
             
          
           
               
                   
                 for (k=1; k&lt;M−1; k++) { 
               
             
          
           
               
                   
                 A[i][j][k] = C0*B[i][j][k] + C1*(sum(B[...][...][...]); 
               
               
                   
                 // S0(i,j,k); 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                 for (i=1; i&lt;N−1; i++) { 
               
             
          
           
               
                   
                 for (j=1; j&lt;N−1; j++) { 
               
             
          
           
               
                   
                 for (k=1; k&lt;M−1; k++) { 
               
             
          
           
               
                   
                 B[i][j][k] = C0*A[i][j][k] + C1*(sum(A[...][...][...])); 
               
               
                   
                 // S1(i,j,k); }}}} 
               
               
                   
                   
               
             
          
         
       
     
     When computing a schedule for the first level of parallelism (the multiple cores) our invention may produce the following optimized code in which permutable loops are marked as such. 
                                                                                                             for (i=0; i&lt;=Titer−1 ; i++) /* perm*/ {                for (j=0; j&lt;=254; j++) /* perm */ {                for (k=max(j−253, 2*i); k&lt;=min(2*i+254,j+253); k++) /* perm           */ {                for (l=max(2*i, k+−253, j−253); l&lt;=min(j+254, 2*i+255,           k+254); l++) /* perm */ {                if (j&lt;=253 &amp;&amp; 2*i−k&gt;=−253 &amp;&amp; 2*i−l&gt;=−254) {                S0(j+1, k−2*i+1, l−2*i+1);                }           if (j&gt;=1 &amp;&amp; −2*i+k&gt;=1 &amp;&amp; −2*i+l&gt;=1) {                S1(j, k−2*i,l−2*i);            }}}}}                    
In this form, the loops have been fused at the innermost level on loop I and the locality is optimized. Loop tiling by tiling factors (16, 8, 8, 1) may be applied to further improve locality and the program would have the following form, where the inner loops m, n, o are permutable.
 
                                                                                                                                     for (i=0; i&lt;=floorDiv(Titer −1, 16); i++) { /* perm*/                for (j=2*i; j&lt;=min(2*i+17, floorDiv(Titer+126, 8)); j++) { /* perm*/                for (k=max(2*i, j−16); k &lt;= min(floorDiv(Titer+126, 8), j+16,           2*i+17); k++) { /* perm*/                for (l=max(16*i, 8*k−127, 8*j−127); l&lt;=min(Titer−1,           8*k+7, 16*i+15, 8*j+7); l++) {                 /* perm*/           for (m .....) /* perm */                for (n .....) /* perm */                for (o .....) /* perm */                if (condition1) {                S0(m,n,o)                }           if (condition2) {                S1(m,n,o); }}}}}}}}}                        
Without further optimization, the loops are fused on all loops i,j,k,l,m,n and o. The program does not take advantage of fine grained parallelism on each processor along the loops m, n and o. Our innovation allows the optimization of another selective tradeoff to express maximal innermost parallelism at the expense of fusion. The selective tradeoff gives a much more important cost to parallelism than locality and our innovation may finds a different schedule for the intra-tile loops that result in a program that may display the following pattern:
 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
               
             
             
               
                 for (i=0; i&lt;=floorDiv(Titer −1, 16); i++) { /* perm */ 
               
             
          
           
               
                   
                 for (j=2*i; j&lt;=min(2*i+17, floorDiv(Titer+126, 8)); j++) { /* perm */ 
               
             
          
           
               
                   
                 for (k=max(2*i, j−16); k &lt;= min(floorDiv(Titer+126, 8), j+16, 2*i+17); k++) { /* perm */ 
               
             
          
           
               
                   
                 for (l=max(16*i, 8*k−127, 8*j−127); l&lt;=min(Titer−1, 8*k+7, 16*i+15, 8*j+7); l++) { 
               
               
                   
                 /* perm */ 
               
             
          
           
               
                   
                 if (−8*k+l&gt;=−126) { 
               
             
          
           
               
                   
                 doall (m = max(0, 16 * j −2 * l); m &lt;= min(16 * j −2 * l + 15, 253); m++) { 
               
             
          
           
               
                   
                 doall (n = max(0, 16 * k −2 * l); n &lt;= min(16 * k −2 * l + 15, 253); n++) { 
               
             
          
           
               
                   
                 doall (o = 0; o &lt;= 254; o++) { 
               
             
          
           
               
                   
                 S0(1 + m,1 + n,1 + o); 
               
             
          
           
               
                   
                 }}}} 
               
               
                   
                 doall (m=max(0, 16*j−2*l−1); m&lt;=min(16*j−2*l+14, 253); m++) { 
               
             
          
           
               
                   
                  doall (n=max(16*k−2*l−1, 0); n &lt;= min(253, 16*k−2*l+14); n++) { 
               
             
          
           
               
                   
                  doall (o=0; o&lt;=254; o++) { 
               
             
          
           
               
                   
                  S1(1 + m,1 + n,1 + o); 
               
             
          
           
               
                   
                 }}}}}}} 
               
               
                   
                   
               
             
          
         
       
     
     The innermost doall dimensions may further be exploited to produce vector like instructions while the outermost permutable loops may be skewed to produce multiple dimensions of coarse grained parallelism. 
     In a further embodiment, the schedules that produce the innermost doall dimensions may be further used to produce another level of multi-level tiling hyperplanes. The resulting code may have the following structure: 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
             
           
               
                   
               
             
             
               
                 for (i=0; i&lt;=floorDiv(Titer −1, 16); i++) { /* perm */ 
               
             
          
           
               
                   
                 for (j=2*i; j&lt;=min(2*i+17, floorDiv(Titer+126, 8)); j++) { /* perm */ 
               
             
          
           
               
                   
                 for (k=max(2*i, j−16); k &lt;= min(floorDiv(Titer+126, 8), j+16, 2*i+17); k++) { 
               
             
          
           
               
                   
                 /* perm */ 
               
               
                   
                 for (l=max(16*i, 8*k−127, 8*j−127); l&lt;=min(Titer−1, 8*k+7, 16*i+15, 8*j+7); l++) { 
               
               
                   
                 /* perm */ 
               
             
          
           
               
                   
                 if (−8*k+l&gt;=−126){ 
               
             
          
           
               
                   
                 doall (m ...) { 
               
             
          
           
               
                   
                 doall(n ...) { 
               
             
          
           
               
                   
                 doall (o ...) { 
               
             
          
           
               
                   
                 doall (p ...) { 
               
             
          
           
               
                   
                 doall (q ...) { 
               
             
          
           
               
                   
                 doall (r ...) { 
               
             
          
           
               
                   
                 S0(1 + p,1 + q,1 + r); 
               
             
          
           
               
                   
                 }}}}}}} 
               
               
                   
                 doall (m ...) { 
               
             
          
           
               
                   
                  doall (n ...) { 
               
             
          
           
               
                   
                  doall(o ...) { 
               
             
          
           
               
                   
                 doall (p ...) { 
               
             
          
           
               
                   
                 doall (q ...) { 
               
             
          
           
               
                   
                 doall (r ...) { 
               
             
          
           
               
                   
                 S1(1 + p,1 + q,1 + r); 
               
             
          
           
               
                 }}}}}}}}}} 
               
               
                   
               
             
          
         
       
     
     In the following example, dependencies between the loop nests prevent the loops from being fused directly, unless loop shifting is used to peel extra iterations off the first and second loops. The resulting transformation is illustrated in the code below. 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 if (M &gt;= 0) { 
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= N; i++) { 
               
             
          
           
               
                   
                 for (int j = −2; j &lt;= min(M + −2, −1); j++) { 
               
             
          
           
               
                   
                 A[i][2 + j] = f(C[−2 + i][3 + j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = 0; j &lt;= M + −2; j++) { 
               
             
          
           
               
                   
                 A[i][2 + j] = f(C[−2 + i][3 + j]); 
               
               
                   
                 B[i][j] = g(A[i][1 + j], A[i][j], C[−1 + i][j]); 
               
               
                   
                 C[i] [j] = h(B[i][j], A[i][2 + j], A[i][l + j]); 
               
             
          
           
               
                   
                 } 
               
               
                   
                 for (int j = max(O, M + −1); j &lt;= M; j++) { 
               
             
          
           
               
                   
                 B[i] [j] = g(A[i] [1 + j], A[i][j], C[−1 + i][j]); 
               
               
                   
                 C[i] [j] = h(B[i] [j], A[i] [2 + j], A[i] [1 + j]); 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                   
               
             
          
         
       
     
     On the other hand, affine fusion (i.e., fusion combined with other affine transformations) gives a superior transformation, as shown below. In this transformation, the fusion-preventing dependencies between the loop nests are broken with a loop reversal rather than loop shifting, and as a result, no prologue or epilogue code is required. Furthermore, the two resulting loop nests are permutable. In some embodiments, tiling and extraction of one degree of parallelism out of the resulting loop nests is performed. 
     
       
         
               
             
               
               
             
               
               
             
               
               
             
               
             
           
               
                   
               
             
             
               
                 if (M &gt;= 0) { 
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= N; i++) { ll permutable 
               
             
          
           
               
                   
                 for (int j = − M; j &lt;= 0; j++) { ll permutable 
               
             
          
           
               
                   
                 A[i] [ − j] f(C[−2 + i][1 − j]); 
               
               
                   
                 B[i][ − j] g(A[i][1 − j], A[i][ − j], C[−1 + i][− j]); 
               
               
                   
                 C[i] [j] h(B[i] [ − j], A[i] [2 j], A[i] [1 − j]); 
               
             
          
           
               
                 }}} 
               
               
                   
               
             
          
         
       
     
     In some embodiments loop fusion is limited to not be too greedy, i.e., aggressive fusion that destroys parallelism should be avoided. On the other hand, fusion that can substantially improve locality may sometimes be preferred over an extra degree of parallelism, if we already have; obtained sufficient degrees of parallelism to exploit the hardware resources. For example, given the following code: 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 for (int i = 0; l &lt;= −1 + n; i++{ 
               
             
          
           
               
                   
                 for (int j = 0; j &lt;= −1+n j++) { 
               
             
          
           
               
                   
                 C[i] [j] = 0; }} 
               
             
          
           
               
                   
                 for (int i = 0; i &lt;= −1 + n; i++) { 
               
             
          
           
               
                   
                 for (int j = 0; j &lt;= −1 + n; j++) { 
               
             
          
           
               
                   
                 for (int k 0; k &lt;= −1 + n; k++) { 
               
             
          
           
               
                   
                 C[i] [j] = C[i] [j] + A[i] [k] * B[k] [j]; 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                 for (int i = 0; i &lt;= −1 + n; i++) { 
               
             
          
           
               
                   
                 for (int j = 0; j &lt;= −1 + n; j++) { 
               
             
          
           
               
                   
                 for (int k 0; k &lt;= −1 + n; k++) { 
               
             
          
           
               
                   
                 D[i] [j] = D[i] [j] + C[k] [j] * E[i] [j]; 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                   
               
             
          
         
       
     
     If fusion is applied too aggressively, it gives up an additional level of synchronization-free parallelism. 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 doall (int i = 0; i &lt;= n + −1; i++) { 
               
             
          
           
               
                   
                 for (int j = 0; j &lt;= n + −1; j++) { 
               
             
          
           
               
                   
                 C[j] [i] = 0; 
               
               
                   
                 for (int k = 0; k &lt;= n+−1; k++{ 
               
             
          
           
               
                   
                 C[j] [i] = C[j][i] + A[j][k * B[k][i] 
               
             
          
           
               
                   
                 } 
               
               
                   
                 doall (int k = 0; k &lt;= n + −1; k++) { 
               
             
          
           
               
                   
                 D[k] [i] = D[k] [i] + C[j] [i] * E[k] [i] ; 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                   
               
             
          
         
       
     
     The below code illustrates the result of only applying fusion that does not destroy parallelism. The two inner j-loops are fissioned in this transformation, exposing a second level of synchronization-free parallelism. 
     
       
         
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 doall (int i = 0; i &lt;= n + −1; i++) { 
               
             
          
           
               
                   
                 doall (int j = 0; j &lt;= n + −1; j++) 
               
             
          
           
               
                   
                 C[j] [i] = 0; 
               
               
                   
                 for (int k 0; k &lt;= n + −1; k++) { 
               
             
          
           
               
                   
                 C[j] [i] = C[j] [i] + A[j] [k] * B[k] [i]; 
               
             
          
           
               
                   
                 }} 
               
               
                   
                 doall (int j = 0; j &lt;= n + −1; j++) { 
               
             
          
           
               
                   
                 for (int k 0; k &lt;= n + −1; k++) { 
               
             
          
           
               
                   
                 D[j] [i] = D[j] [i] + C[k] [i] * E [j] [i]; 
               
             
          
           
               
                   
                 }}} 
               
               
                   
                   
               
             
          
         
       
     
     The above illustrates that this tension between fusion and scheduling implies that fusion and scheduling should be solved in a unified manner. Turning now to  FIG. 4  where the flow of provided method  100  of source code optimization is illustrated. Flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  120  where a selective tradeoff of parallelism and locality is created for execution of the code on the second computing apparatus  10 ( b ). Flow then continues to block  130  where a scheduling function is produced which optimizes the selective tradeoff. Flow then continues to block  140  where the scheduling function is used to assign a partial order to the statements of the source code and an optimized program is produced for execution on the second computing apparatus  10 ( b ). In one embodiment, the received program code contains at least one arbitrary loop nest. As previously discussed the custom first computing apparatus  10 ( a ) contains memory  50 , a storage medium  60  and at least one processor with a multi-stage execution unit. 
     A provided method  150  for source code optimization is illustrated in  FIG. 5 . In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  120  where the code is optimized in terms of both locality and parallelism for execution on a second computing apparatus  10 ( b ). In this embodiment, the optimization block  120  additionally includes additional functional blocks. Within block  120  flow begins with block  160  where an unassigned loop is identified. Flow then continues on two paths. In a first path flow continues to block  180  where a first cost function is assigned in block  180 . This first cost function is related to a difference in execution speed between parallel and sequential operations of the statements within the loop on second computing apparatus  10 ( b ). Flow then continues to block  210  where a decision variable is assigned to the loop under consideration, this decision variable indicating whether the loop is to be executed in parallel in the optimized program. In some embodiments the cost is determined through static evaluation of a model of the execution cost of the instructions in the loop under consideration. In other embodiments, the cost is determined through a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop under consideration. In a further embodiment, the cost is determined by an iterative refining process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop under consideration. Flow then continues to decision block  220  where it is determined if there are additional unassigned loops. 
     As used herein, “executed together” means fused in the sense of the code examples (0032)-(0037). Specifically executed together means that loops that are consecutive in the original program become interleaved in the optimized program. In particular, loops that are not “executed together” in the sense of loop fusion can be executed together on the same processor in the more general sense. In the second optimization path illustrated in  FIG. 5  flow continues from block  160  to block  170  where an unassigned loop pair is identified. Flow then continues to block  175  where a second cost function is assigned for locality optimization. This second cost function is related to a difference in execution speed between operations where the loops in the pair of loops are executed together on the second computing apparatus, and where the loops in the pair of loops are not executed together on the second computing apparatus. Flow then continues to block  190  where a decision variable is assigned for locality. This second decision variable specifying if the loops in the loop pair under consideration are to be executed together in the optimized program. In one embodiment, the second cost is determined through static evaluation of a model of the execution cost of the instructions in the at least one loop pair. 
     In another embodiment, the second cost is determined through of a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the at least one loop pair. In a further embodiment, the cost is determined through an iterative refining process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the at least one loop pair. Flow then continues to decision block  200  where it is determined if additional unassigned loop pairs exist. If additional unassigned loop pairs exist, flow continues back to block  170  and the process iterates until no additional unassigned loop pairs are found. When decision block  200  determines no additional loop pairs are present, flow continues to decision block  220 . If in decision block  220  it is determined that additional unassigned loops exist, flow continues back to block  160  and the process iterates until no additional unassigned loops may be identified. Flow then continues to block  230  where a selective tradeoff is created for locality and parallelism during the execution on second computing apparatus  10 ( b ). Flow then continues to block  130  where a scheduling function is produced that optimizes the selective tradeoff. Flow then continues to block  140  where optimized code is produced. 
     The flow of a further provided embodiment of a method  240  for source code optimization is illustrated in  FIG. 6 . In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  120  where the code is optimized in terms of both locality and parallelism for execution on a second computing apparatus  10 ( b ). Flow then continues to block  130  where a scheduling function is produced that optimizes the tradeoff. In this embodiment, the scheduling function block  130  additionally includes additional functional blocks. Within block  130  flow continues to block  250  where the conditions for semantic correctness of the program are determined. Flow then continues to block  260  where a search space is derived that meet the conditions for semantic correctness. In one embodiment, the search space characterizes all parallelism and locality opportunities that meet the conditions of semantic correctness. Flow then continues to block  270  where the selective trade off is optimized. Flow then continues to block  280  where the scheduling function is derived from the optimized tradeoff. Flow then continues to block  140  where optimized code is produced. 
     The flow of a further provided method is illustrated in  FIG. 7 . This embodiment illustrates alternate embodiments of the flow within blocks  130  and  270  in previous embodiments. As illustrated, flow begins in block  250  where the conditions for semantic correctness of the program are determined. Flow then continues to block  260  where a search space is derived that meet the conditions for semantic correctness. In one embodiment, the search space characterizes all parallelism and locality opportunities that meet the conditions of semantic correctness. Like previous embodiments, flow then continues to block  270  where the selective trade off is optimized. In these embodiments, block  270  includes additional functionality. Block  270  as illustrated contains three independent optimization paths that may be present in any given embodiment. In the first embodiment, flow begins at block  300 ( a ) where an element is selected from the search space. Flow then continues to block  310 ( a ) where a potential scheduling function is derived for the element. Flow then continues to block  320 ( a ) where the performance of the potential scheduling function is evaluated. Flow then continues to decision block  330 ( a ) where it is determined if additional elements exist in the search space. If additional elements exist, flow continues back to block  300 ( a ). When no additional elements exist in the search space, flow then continues to block  370  where the element with the best evaluated performance is selected. 
     In the second illustrated embodiment, flow continues from block  260  to block  300 ( b ) where an element is selected from the search space. Flow continues to block  310 ( b ) where a potential scheduling function is derived for the element. Flow then continues to block  320 ( b ) where the performance of the potential scheduling function is evaluated. Flow then continues to block  340  where the search space is refined using the performance of evaluated schedules. Flow then continues to decision block  330 ( b ) where it is determined if additional elements exist in the search space. If additional elements are present flow continues back to block  330  and the process iterated until no other elements exist in the search space. When no additional elements exist, in the search space, flow then continues to block  370  where the element with the best evaluated performance is selected. 
     In the third illustrated embodiment, flow continues from block  260  to block  350  where the tradeoff is directly optimized in the search space with a mathematical problem solver. Flow then continues to block  360  where an element is selected that is a result of the direct optimization. Flow then continues to block  320 ( c ) there the performance of the selected element is evaluated. Flow then continues to block  370  where the element with the best evaluated performance is selected. As illustrated some embodiments may utilize more than one of these paths in arriving at an optimal solution. From selection block  370  flow then continues to block  280  where the scheduling function is derived from the optimized tradeoff. Flow then continues to block  140  where optimized code is produced. 
     The flow of a further provided embodiment of a method  380  for optimization of source code on a first custom computing apparatus  10 ( a ) for execution on a second computing apparatus  10 ( b ) is illustrated in  FIG. 8 . In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  400  where the source code is optimized in terms of both locality and parallelism for execution on a second computing apparatus  10 ( b ). In this embodiment, block  400  contains additional functional blocks. Flow continues from block  110  to block  250  where the conditions for semantic correctness are determined from the received code. Flow then continues to block  390  where these conditions are represented as a generalized dependence graph. Flow then continues to two paths. 
     On a first path, flow continues to block  260  where a search space is derived that meet the conditions for semantic correctness. In this embodiment, the search space characterizes all parallelism and locality opportunities that meet the conditions of semantic correctness. Flow then continues to block  410  where a weighted parametric tradeoff is derived and optimized on the elements of the search space. On the second path, flow begins with block  160  where an unassigned loop is identified. Flow then continues on two additional paths. In a first path flow continues to block  180  where a first cost function is assigned in block  180 . This first cost function is related to a difference in execution speed between parallel and sequential operations of the statements within the unidentified loop on second computing apparatus  10 ( b ). Flow then continues to block  210  where a decision variable is assigned to the loop under consideration, this decision variable indicating whether the loop is to be executed in parallel in the optimized program. In some embodiments the cost is determined through static evaluation of a model of the execution cost of the instructions in the loop under consideration. In other embodiments, the cost is determined through a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop under consideration. In a further embodiment, the cost is determined by an iterative refining process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the loop under consideration. Flow then continues to decision block  220  where it is determined if there are additional unassigned loops. 
     Returning to block  160  where an unassigned loop is identified. On the second path flow continues to block  170  where an unassigned loop pair is identified. Flow then continues to block  175  where a second cost function is assigned for locality optimization. This second cost function is related to a difference in execution speed between operations where the loops of the pair of loops are executed together on the second computing apparatus, and where the loops of the pair of loops are not executed together on the second computing apparatus. Flow then continues to block  190  where a decision variable is assigned for locality. This second decision variable specifying if the loops of the loop pair under consideration is to be executed together in the optimized program. In one embodiment, the second cost is determined through static evaluation of a model of the execution cost of the instructions in the at least one loop pair. In another embodiment, the second cost is determined through of a dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the at least one loop pair. In a further embodiment, the cost is determined through an iterative refining process consisting of at least one static evaluation of a model of the execution cost and at least one dynamic execution on the second computing apparatus of at least a set of instructions representative of the code in the at least one loop pair. Flow then continues to decision block  200  where it is determined if additional unassigned loop pairs exist. If additional unassigned loop pairs exist, flow continues back to block  170  and the process iterates until no additional unassigned loop pairs are found. When decision block  200  determines no additional loop pairs are present, flow continues to decision block  220 . If in decision block  220  it is determined that additional unassigned loops exist, flow continues back to block  160  and the process iterates until no additional unassigned loops may be identified. Flow then continues to block  230  where a selective trade-off is created for locality and parallelism during the execution on second computing apparatus  10 ( b ). 
     In this embodiment, flow then continues to block  410  where as discussed, a weighted parametric tradeoff is derived and optimized on the elements of the search space. Flow then continues to block  420  where a multi-dimensional piecewise affine scheduling function is derived that optimizes the code for execution on second computing apparatus  10 ( b ). Flow then continues to block  140  where the optimized program is produced. 
     The operational flow of a further provided method  430  for source code optimization is illustrated in  FIG. 9 . In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  480  where the a level of parallelism and memory hierarchy in the second computing apparatus are selected. Flow then continues to block  490  where a selective tradeoff for parallelism and locality for execution of that level of hierarchy is created. Flow then continues to block  440  where a piecewise affine multi-dimensional scheduling function is derived that optimizes the specific tradeoff. Flow then continues to block  450  where tiling hyper-planes are produced based on the scheduling function. Flow then continues to decision block  460  where it is determined if additional levels of parallelism and memory hierarchy exist on second computing apparatus  10 ( b ). If additional levels exist, flow continues back to block  480  and the process iterates until it is determined that no additional levels exist. Flow then continues to block  470  where the scheduling functions and tiling hyper-planes are used to assign a partial order to the statements of the source code and an optimized program is produced. In some embodiments, a global weighted parametric function is used to optimize each level of parallelism and hierarchy on second computing apparatus  10 ( b ). 
     The operational flow of a further provided method  500  for source code optimization is illustrated in  FIGS. 10(   a ) and  10 ( b ). In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  250  where the conditions for semantic correctness are determined for the program. Flow then continues to block  390  where these conditions are represented as a generalized dependence graph. Alternatively as indicated in block  510  schedule dimensions may have been found through the methods disclosed in other embodiments. Flow continues to block  520  where the generalized dependence graph is decomposed into at least one strongly connected component. Flow then continues to block  530  where a strongly connected component is selected. Flow then continues to a number of independent paths. 
     In the first path, flow continues to block  540  where a set of affine constraints are derived using the affine form of Farkas lemma. On the second path, flow continues to block  550  where linear independence constraints are derived and used to ensure the successive scheduling dimensions are linearly independent. In some embodiment, these linear independence constraints are derived using orthogonally independent subspaces. In another embodiment, these constraints are formed using a Hermite Normal form decomposition. In the third path, flow continues to block  560  where a set of schedule difference constraints are derived and used to enforce dimensions of schedules of loops belonging to the same strongly connected component are permutable. In the last path, a set of loop independence constraints are derived and used to ensure that dimensions of schedules of loops that are not executed together do not influence each other. In one embodiment, this set of constraints includes a large enough constraint to cancel an effect of constraints on statements that are not executed together in the optimized program. 
     Flow then continues to block  580  where these derived constraints are added to the search space. Flow then continues to decision block  590  where it is determined if there are additional strongly connected components. If there are additional strongly connected components, flow continues back to block  530  and the process iterates until there are no further strongly connected components. Flow then continues to block  260  where a search space is derived that characterizes all parallelism and locality opportunities that meet the conditions of semantic correctness. Flow then proceeds to block  600  where a weighted parametric tradeoff is optimized on the elements of the search space. Flow continues to block  420  where a multi-dimensional piecewise affine scheduling function is derived from the optimization and to block  140  where this function is used to create an optimized program for execution on second computing apparatus  10 ( b ). In one embodiment, the optimization can reach any legal dimensional affine scheduling of the received program. In another embodiment, the legal multi-dimensional affine scheduling of the received program includes loop reversals. 
     The operational flow of a further provided method  610  for source code optimization is illustrated in  FIG. 11 . As with other embodiments, this embodiment may be used in conjunction with other provided methods. In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  780  which contains additional functionality. Flow continues to block  250  where the conditions for semantic correctness are determined for the program. Flow then continues to block  390  where these conditions are represented as a generalized dependence graph. Flow then continues to decision block  620  where it is determined if there are additional dimensions to schedule. If there are no additional dimensions, flow continues to block  760  where a scheduling function is derived and to block  140  where an optimized program is produced for second computing apparatus  10 ( b ). 
     If at decision block  620  determines that there are additional scheduling dimensions, flow continues to block  630  where the generalized dependence graph is decomposed into at least one strongly connected component. Flow continues to block  640  where a strongly connected component is selected. Flow then continues to block  650  where affine constraints are derived using the affine form of Farkas lemma, linear independence constraints permutability constraints, and independence constraints are derived as previously discussed. Flow then continues to block  660  where these constraints are added to the search space. Flow then continues to decision block  670  where it is determined if additional strongly connected components exits. If others exist, flow continues back to  640  and the process iterates until there are no remaining strongly connected components. 
     When decision block  670  indicates that there are no remaining strongly connected components, flow continues to block  730  where a weighted parametric tradeoff function is optimized on the search space. Flow then continues to decision block  690  where it is determined if new independent permutable schedule dimensions exist. If they exist flow continues to block  700  where an existing scheduling dimension is selected. Flow continues to block  720  where additional constraints are added to the search space for independence and linear independence. From block  720  flow continues to block  730  where a weighted parametric tradeoff function is optimized on the search space. Flow then continues back to decision block  690  and this part of the process iterates until no new independent permutable schedule dimensions are found. Flow then continues to block  740  where satisfied edges are removed from the dependence graph and to block  750  where the remaining edges and nodes are partitioned into smaller dependence graphs. Flow then continues back to block  390  and the process is iterated on these smaller dependence graphs until decision block  620  determines there are no additional dimensions to schedule. 
     The flow of a further provided embodiment of a method  760  for optimization of source code on a first custom computing apparatus  10 ( a ) for execution on a second computing apparatus  10 ( b ) is illustrated in  FIGS. 12(   a ) and  12 ( b ). In this embodiment, flow begins in block  110  where source code is received in memory  50  on a custom first computing apparatus  10 ( a ). On a first path flow continues to block  120  where a selective tradeoff of parallelism and locality for execution of the program on second computing apparatus  10 ( b ) is created. Flow continues to block  250  where the conditions for semantic correctness are determined. Flow continues to block  770  where a single multi-dimensional convex space of all legal schedules is derived. Additional information on block  770  is provided in  FIG. 12(   b ). Like some previous embodiments, flow then continues on alternate three paths. On the first path flow continues to block  790 ( a ) where a element from the search space is selected. Flow then continues to block  800 ( a ) where a scheduling function is derived for the selected element. Flow then continues to block  810 ( a ) where the scheduling function is evaluated for its performance on the optimized program. Flow continues to decision block  820 ( a ). If it is determined that there are additional elements in the search space, flow continues back to block  790 ( a ) where another element is selected. The process iterates until no additional elements remain in the search space. 
     On the second path flow continues to block  790 ( b ) where an element of the search space is selected. Flow then continues to block  800 ( b ) where a scheduling function is derived for the selected element. Flow then continues to block  810 ( b ) where the performance of the scheduling function is evaluated. Flow then continues to block  830  where the search space is refined using the performance of evaluated schedules. Flow then continues to decision block  820 ( b ). If there are additional elements remaining in the search space flow continues back to block  790 ( b ) and another element is selected from the search space. The process iterates until there are no remaining elements in the search space. 
     On the third path flow continues to block  840  where the selective tradeoff is directly optimized using a mathematical solver. Flow then continues to block  850  where an element is selected from the search space that is a solution to the optimization. Flow then continues to block  860  where the performance of the selected element is evaluated. Flow then continues to block  870  which selects the element with the best evaluated performance for all of its inputs. Flow then continues to block  880  which produces a scheduling function from the selective tradeoff and the selected element. Flow then continues to block  890  where the scheduling function is used to assign a partial order to the statements of the source code and an optimized program is produced. 
     An exemplary embodiment of block  770  is illustrated in  FIG. 12(   b ). In this embodiment, flow from block  250  continues to block  390  where the conditions for semantic correctness are represented as a generalized dependence graph. Flow continues on two parallel paths. On the first path an edge E is selected from the dependence graph in block  900 . Flow then continues to block  910  where a strong satisfaction variable is assigned to edge E at dimension K. Block  910  receives the current dimension K from block  1010 . Flow then continues to block  930  where multi-dimensional constraints are derived to ensure independence of the nodes linked by edge E if scheduling is satisfied before dimension K. Flow then continues to decision block  940 . If there are additional edges in the dependence graph flow continues back to block  900  where another edge is selected and the process iterates until no additional edges exist. 
     On the second path, flow continues from block  390  to block  970  where a node N is selected. Flow continues to block  980  where a statement permutability variable is assigned to node N at dimension K. Block  980  receives dimension K from block  1010 . Flow continues to decision block  990 . If there are remaining nodes in the dependence graph flow continues back to block  970  where another node N is selected. The process iterates until no additional nodes exist in the graph. Block  950  receives input from blocks  920  and  980  and assigns constraints to link edge permutability variable and statement permutability variable at dimension K. Flow then continues to block  960  where constraints to equate statement permutability variables for source and sink of edge E at dimension K are assigned. Flow then continues to decision block  1000 . If additional scheduling dimensions exist, flow continues back to block  1010  the next scheduling dimension is selected and the entire process repeated for all dimensions. When all dimensions have been scheduled, flow continues to block  1020  where a single multi-dimensional convex affine space is constructed from all of the legal schedules. 
     The flow of another provided method  1070  for program code optimization is illustrated in  FIG. 13 . In this method, flow begins in block  110  where program source code is received in memory  50  on a custom first computing apparatus  10 ( a ). Flow continues to block  1080  where a level of parallelism and memory hierarchy is selected from the second computing apparatus  10 ( b ). Flow then continues to block  780  which is illustrated in  FIG. 11  and discussed in detail above. Flow then continues to decision block  1020 . If the performance of the scheduling function is not satisfactory for the current level, flow continues to block  1030  where a partial evaluation of the code produced for the current level of parallelism and memory hierarchy is performed and used to iteratively refine the schedule. Flow continues back to block  780  a iterates until the performance of the schedule is satisfactory for the level. Flow then continues to block  1040  where tiling hyper-planes are produced based on the scheduling function. Flow then continues to decision block  1050 . If there are additional levels of parallelism and memory hierarchy flow continues back to block  1080  and the process iterates. Once no additional levels exist, flow continues to block  1060  where the scheduling functions and tiling hyper-planes are used to assign a partial order to the statements of the source code and an optimized program is produced. 
     Thus, it is seen that methods and an apparatus for optimizing source code on a custom first computing apparatus for execution on a second computing apparatus are provided. One skilled in the art will appreciate that the present invention can be practiced by other than the above-described embodiments, which are presented in this description for purposes of illustration and not of limitation. The specification and drawings are not intended to limit the exclusionary scope of this patent document. It is noted that various equivalents for the particular embodiments discussed in this description may practice the invention as well. That is, while the present invention has been described in conjunction with specific embodiments, it is evident that many alternatives, modifications, permutations and variations will become apparent to those of ordinary skill in the art in light of the foregoing description. Accordingly, it is intended that the present invention embrace all such alternatives, modifications and variations as fall within the scope of the appended claims. The fact that a product, process or method exhibits differences from one or more of the above-described exemplary embodiments does not mean that the product or process is outside the scope (literal scope and/or other legally-recognized scope) of the following claims.