The present invention relates generally to program compilers, and, more particularly, to multi-threaded compilation of computer programs.
A compiler is a computer program that translates a source code language into a target language. Typically, though not always, the source code is written in a high level language and the target language is a low level object code.
As further illustrated in
Linker/code generator 214 performs link time code generation (LTCG). Link time code generation enables a variety of code optimizations to be performed, including optimizations that take advantage of knowledge of different functions, which may be in different modules. This is sometimes referred to as inter-procedural optimization. For example, a code generator may insert code to save certain registers prior to each function call and restore the registers upon returning from the function call. If a link time code generator can determine that a particular function does not change a specific register, it may avoid generating the register save and restore instructions around each invocation of that function, even if invoked from a different module. Another example of an optimization is to insert the code of a very short function inline where invoked, rather than insert instructions to call the function. By having knowledge of a first function when referenced by a second function, a code generator that operates during link time may perform inter-procedural optimizations such as these.
This Summary is provided to introduce a selection of concepts in a simplified form that are further described below in the Detailed Description. This Summary is not intended to identify key features or essential features of the claimed subject matter, nor is it intended to be used to limit the scope of the claimed subject matter.
Briefly, a system, method, and components operate to compile a computer program using multiple threads. This may include using multiple threads to generate code for a plurality of source functions, where at least some of the threads execute concurrently, so that code is generated concurrently for some of the source functions. Code generation may perform various types of optimization, and may include optimizing the code based on knowledge of other functions, or inter-procedural code optimization.
A system may include a dependency analyzer component that analyzes dependencies among the functions. In one implementation, this may include creating a directed acyclic graph (DAG) based on references of the functions. This may include creating a directed graph, with nodes representing functions and edges representing dependencies, and breaking one or more dependencies in order to create a DAG. The system may include a code generator, which can be instantiated to create two or more code generator instances, in which each code generator instance executes in a respective thread and generates code for a respective function.
The system may include a scheduler component that schedules each code generator instance based on the DAG, so that at least two of the code generator instances may execute concurrently. The system may include an assembler component that aggregates the generated code of each function based on one or more sort keys, to create an aggregation of generated code in an ordering that is deterministically based on the source functions.
One aspect of the system is to deterministically create a binary file, such as an application executable, based on the input source files, such that compiling and generating code multiple times results in an identical output binary file each time, provided that the input source files and compiler specifications remain unchanged. The output is deterministic regardless of differences in a sequence of operations that may occur due to multi-threading and varying configurations. Individual object files may be deterministically produced as an intermediate step in creating an application executable.
In one aspect of the system, a code generator instance may perform one or more optimizations of code for a function based on information obtained from compiling another function. In one aspect of the system, information obtained from compiling a function may be selectively used to optimize code in another function, based on whether a broken dependency corresponding to either function exists, or more specifically, whether a dependency, which may be direct or indirect, of the other function on the function exists.
A system may employ mechanisms described herein to create an application executable that is deterministically based on the computer program, though the sequences of multi-threaded code generation may vary.
To the accomplishment of the foregoing and related ends, certain illustrative aspects of the invention are described herein in connection with the following description and the annexed drawings. These aspects are indicative, however, of but a few of the various ways in which the principles of the invention may be employed and the present invention is intended to include all such aspects and their equivalents. Other advantages and novel features of the invention may become apparent from the following detailed description of the invention when considered in conjunction with the drawings.
Non-limiting and non-exhaustive embodiments of the present invention are described with reference to the following drawings. In the drawings, like reference numerals refer to like parts throughout the various figures unless otherwise specified.
To assist in understanding the present invention, reference will be made to the following Detailed Description, which is to be read in association with the accompanying drawings, wherein:
The present invention now will be described more fully hereinafter with reference to the accompanying drawings, which form a part hereof, and which show, by way of illustration, specific exemplary embodiments by which the invention may be practiced. This invention may, however, be embodied in many different forms and should not be construed as limited to the embodiments set forth herein; rather, these embodiments are provided so that this disclosure will be thorough and complete, and will fully convey the scope of the invention to those skilled in the art. Among other things, the present invention may be embodied as methods or devices. Accordingly, the present invention may take the form of an entirely hardware embodiment, an entirely software embodiment or an embodiment combining software and hardware aspects. The following detailed description is, therefore, not to be taken in a limiting sense.
Throughout the specification and claims, the following terms take the meanings explicitly associated herein, unless the context clearly dictates otherwise. The phrase “in one embodiment” as used herein does not necessarily refer to the same embodiment, though it may. Furthermore, the phrase “in another embodiment” as used herein does not necessarily refer to a different embodiment, although it may. Thus, as described below, various embodiments of the invention may be readily combined, without departing from the scope or spirit of the invention. Similarly, the phrase “in one implementation” as used herein does not necessarily refer to the same implementation, though it may.
In addition, as used herein, the term “or” is an inclusive “or” operator, and is equivalent to the term “and/or,” unless the context clearly dictates otherwise. The term “based on” is not exclusive and allows for being based on additional factors not described, unless the context clearly dictates otherwise. In addition, throughout the specification, the meaning of “a,” “an,” and “the” include plural references. The meaning of “in” includes “in” and “on.”
The components may execute from various computer readable media having various data structures thereon. The components may communicate via local or remote processes such as in accordance with a signal having one or more data packets (e.g. data from one component interacting with another component in a local system, distributed system, or across a network such as the Internet with other systems via the signal). Computer components may be stored, for example, on computer readable media including, but not limited to, an application specific integrated circuit (ASIC), compact disk (CD), digital versatile disk (DVD), read only memory (ROM), floppy disk, hard disk, electrically erasable programmable read only memory (EEPROM), flash memory, or a memory stick in accordance with embodiments of the present invention.
As used herein, the term “thread” refers to a thread of execution. A thread may be a software thread or a hardware thread. In a hardware multi-threaded processor, two or more threads may concurrently exist on the processor. Some processors provide multiple sets of registers or other components, so that multiple hardware threads may each have their own set of registers. A hardware multi-threaded processor may have a number of software threads that is greater than the number of hardware threads it supports. An operating system may manage the software threads, providing each a turn at executing as a hardware thread.
As used herein, a multi-threaded system is a system that supports multiple threads, which may be software or hardware threads. A multi-threaded system may or may not have hardware support for multi-threading.
As used herein, the term “function” refers to a portion of code within a larger program that performs a specific task, and can execute relatively independent of other portions of the program. A function may, but does not necessarily, return a value. In various computer languages, different terms may be used, such as subroutine, method, procedure, or subprogram. As used herein, the term “function” may include all of these.
An application executable is a data object that includes program instructions, typically in a binary formatted file. Generally, the program instructions are a machine code and correspond to processor instructions, though the program instructions may be in another format that is emulated by a processing system. An application executable may include various types of data related to program instructions, such as symbols, debugging information, exception information, strings, resources or the like. A compilation system may produce a single executable file, or it may produce a primary executable file, one or more library files, or associated resource files. As used herein, the term application executable may include one or more executable files, associated files, or a combination thereof. An application executable may also be one or more memory blocks, objects in a database, or other forms of data.
In a compilation system, such as compilation systems 100 and 200 of
In some implementations, it may be acceptable to have a deterministic output that is not exactly identical each time, but is nearly identical. Differences may be within a tolerance acceptable by a user, or may even be due to a desirable feature, such as inclusion of a timestamp. Such differences are contemplated and are within the scope of the invention as described and claimed. Thus, the term “deterministic output” allows for such minor variations in the application executable.
Multi-threaded compilation system 300 is only one example of a suitable system and is not intended to suggest any limitation as to the scope of use or functionality of the present invention. Thus, a variety of system configurations may be employed without departing from the scope or spirit of the present invention.
As illustrated, multi-threaded compilation system 300 includes linker 302. Linker 302 may perform many of the operations of a conventional linker. Linker 302 may also, upon receiving input objects, determine whether the objects include object code or intermediate language representations and, if the objects include IL representations, send them to the multi-threaded code generator 304 for further processing.
Multi-threaded code generator 304 may receive IL representations corresponding to various functions, and process them. Following is a brief introduction to each component. A more detailed discussion of the mechanisms employed is provided below. As illustrated, multi-threaded code generator 304 includes dependence analyzer 306. Briefly, dependence analyzer 306 analyzes dependencies among the program functions it receives. In one implementation, it may generate a directed graph of dependencies among the program functions, and may further process the directed graph to produce a directed acyclic graph (DAG). In one implementation, dependence analyzer 306 includes a dependence breaker component 307 that performs actions related to converting a directed graph to a DAG. Operations of dependence analyzer 306 are described in further detail herein.
Multi-threaded code generator 304 may further include scheduler 308 and one or more instances of a code generator, each instance referred to herein as code generator instance 310. Briefly, scheduler 308 may schedule each function to be processed by code generator instance 310. It may, for example, insert each function in a queue when the function is ready to be processed. Functions may be removed from the queue and processed by code generator instance 310, thereby enforcing an ordering of processing and limiting concurrent processing to those functions that have been inserted into the queue. Scheduler 308 may determine an ordering to schedule functions based on the analysis produced by dependence analyzer 306. For example, in one implementation, dependence analyzer 306 may generate a hierarchical structure such as a DAG, and scheduler 308 may employ a bottom-up scheduling, such that leaves of the directed graph may be processed prior to nodes at a higher level. Further at any state of the graph, two or more leaves of the DAG may be processed concurrently. Leaves of the DAG may be conceptually removed from the DAG as they are processed by the code generator instance 310, so that each iteration of code generation processes a leaf of the DAG that remains. This process is illustrated and discussed in more detail in
Though not illustrated, in one embodiment, scheduler 308 may employ a top-down scheduling, such that roots of the directed graph may be processed prior to nodes at a lower level. Inter-procedural optimizations may include optimizations that use information on calling functions to optimize code of a called function. For example, a called function might be optimized if it is known that all of its caller functions pass a specific constant as an argument, or pass constants within a known range of values. Similarly, other optimizations may be available if characteristics of calling functions are known.
Code generator instance 310 may process each function it receives, generating “contributions.” Contributions may include object code, symbol tables, debugging data, exception information, fix up data, unwind information, or the like. Briefly, fix up data refers to data, such as a symbol, that needs to be resolved by the linker. Unwind information is used by a runtime library to perform actions, such as unwinding the runtime stack when there is an exception. The contributions are generally data that may be included in a final executable binary or associated data object, or are used at a subsequent processing stage, such as by the linker. Multiple code generator instances 310 may each operate in a respective thread, so that in various configurations there may be one or more code generating threads of operation. The number of threads may vary according to the hardware or software configuration, available resources, other processes active on the computing system, or various other factors. Thus, for example, at various times there may be one, two, or more code generators active.
The threads of code generator instance 310 may be software threads or hardware threads. Each thread may execute in a single-threaded or multi-threaded processor. Code generator instance 310, as well as other components of multi-threaded compilation system 300, may execute on a single-threaded or multi-threaded processor architecture. Different code generator instances 310 may execute on respective threads in the same core, different cores within the same processor, or different cores on different processors.
In some implementations, processing a function by code generator instance 310 may be performed by creating a thread to perform code generation tasks corresponding to the particular function. The thread may be terminated upon completion. In some instances, a thread may be reused. That is, a code generator instance 310 may process a function within a thread and, upon completion of actions corresponding to the function, receive a new function for processing (or wait for a new function if one is not ready to be processed or if the system configuration requires the thread to wait). Mechanisms described herein may be used with these or other variations, or combinations thereof.
A code generator component implemented in a thread is referred to as an “instance” of the code generator. As discussed herein, two or more instances of the code generator may, within the restrictions described, perform actions concurrently. Multiple active threads may perform actions concurrently, whether they are software threads or hardware threads, or whether they are on the same or different processing units. As used herein, the term concurrent refers to actions that may include partial concurrency, interleaved processing such as by time-slicing, or processes whereby one thread may actually be in a wait state while another thread is performing instructions. Thus the term concurrent includes variations of concurrency as is generally understood with respect to software and hardware threads.
In a compilation system in which portions of the compilation may be performed by concurrent threads, issues relating to producing deterministic output may arise. For example, when generating or optimizing code for a first function that calls a second function, various optimizations may be performed if the system has information relating to the second function, such as which registers the second function uses, the length of the second function, whether the second function modifies a global variable, or the like. If a multi-threaded system may process either the first function or the second function before the other, the resultant output, and specifically the application executable, may differ, and is therefore not deterministic. As discussed herein, mechanisms of the invention operate to perform multi-threaded compilation and optimization, while producing deterministic output.
One such mechanism is the queue discussed above. The scheduler 308 may include logic that determines, based on the analysis performed by the dependence analyzer 306, which functions may be safely placed in the queue at any particular time, such that the functions may be processed concurrently, while facilitating a deterministic output. The scheduler 308 further determines an ordering of functions to process, based on the analysis of the dependence analyzer 306.
In the embodiment illustrated by
In one implementation, assembler 312 may produce one or more object files or data objects corresponding to each input source module. The output of assembler 312 may be passed back to linker 302 to perform linking operations, such as resolving memory or symbol references, relocating code, or other actions. In one implementation, linker 302 may output a binary file, such as an application executable.
The components of multi-threaded compilation system 300 are presented to show functional or logical aspects of a multi-threaded compilation system. However, these logical components may be implemented in a variety of ways. They may be implemented as separate software components or hardware and software combinations, integrated into a single component, or physically divided in a number of ways. Reference to each component herein refers to a logical component and includes the various configurations that may be used to implement the functionality discussed. They may be implemented on a single computing device or distributed among multiple computing devices in a variety of configurations.
In brief, one embodiment of a computing device that may be employed includes one or more central processing units, a video display adapter, and a mass memory, all in communication with each other via a bus. Each processor may employ a chip multi-processing architecture (CMP), a symmetric multi-threading (SMT) architecture, or a chip multi-threading (CMT) architecture. Briefly, CMP refers to a processor architecture in which there are multiple processor cores per processor chip. SMT refers to a processor architecture in which a processor core has multiple hardware threads of execution. CMT refers to a processor architecture having multiple processor cores per processor chip and multiple hardware threads of execution per core.
The mass memory may include a random access memory (RAM), a read only memory (ROM), one or more permanent mass storage devices, removable media, or a combination thereof. Mass storage devices may include a hard disk drive, optical drive, flash memory, or a floppy disk drive. The mass memory may include a general-purpose operating system, application programs, security programs, communication programs, or other computer programs.
In accordance with one aspect of the mechanisms described herein, dependence analyzer may analyze the functions that it receives as input to determine relationships among functions based on dependencies between them. In various implementations, the results of the analysis may be represented in a variety of ways. One such representation is a directed graph, which represents dependency relationships between pairs of functions. A directed graph may itself be represented in numerous ways. These include, but are not limited to, a set of binary relations, an adjacency matrix, a linked list, or a text string. As used herein, the term directed graph refers to a logical representation that indicates directed relationships between pairs of functions. It is not limited to a particular representation, unless stated otherwise.
As used herein, the term dependency refers to a reference to the dependee function by the dependent function, such that the reference may indicate information relating to the dependee function that may be useful for the optimization of the dependent function. The reference may be the result of an invocation of the dependee function, a use of a variable, data object, or other element defined by the dependee function, or the like.
Directed graph 400 does not contain any directed cycles, and is therefore a directed acyclic graph (DAG). As discussed for a directed graph, a DAG may be represented in a variety of ways, and reference to a DAG is not limited to any particular representation unless stated otherwise herein. Function D is a leaf node of the DAG, in that it does not have any dependencies on other nodes of the DAG. Function A is a root node of the DAG, in that it is not a dependee of any other function. A DAG may have one or more leaf nodes and one or more root nodes.
Generally, employing concurrency of code generation, by using multiple instances of a code generator, each in a different thread, may improve performance of the compilation process. However, mechanisms described herein may place restrictions on concurrency, and enforce an ordering, in order to facilitate optimization and generate a deterministic result.
Reference to
Assembler 312 (
Column 504 indicates the reference count of each function at a time (0), which is prior to beginning code generation for any of the functions. As can be seen by directed graph 400, and the discussion above, function A 402 has two references, functions B 404 and C 406 have one reference, and function D 408 has zero references. A function having zero references may be ready to be processed by code generator instance 310. In one implementation, this may be performed by inserting function D onto the code generation queue. A code generator instance 310 may retrieve function D from the queue and process it, producing its contributions, including program instructions. In one implementation, a scheduling process may create a new thread, or reuse an existing thread, and pass to the thread a function that is ready to be processed. In one implementation, a code generation thread may retrieve a function that is ready to be processed, process it, and then retrieve another function, blocking when there is not a function that is ready to be processed.
When code generation for function D 408 is completed, the reference count for each function that references function D may be decremented, indicating that the dependency has been satisfied. A dependency is considered satisfied when the dependee function has been processed by the code generator. Prior to that, the dependency is considered unsatisfied. Column 506 indicates the updated reference counts, at time (1), after code generation for function D 408 is completed. As illustrated, reference counts corresponding to functions B and C have been decremented to zero. Note that row 518 representing function D may be logically flagged or removed from the table. In some implementations, function D may be flagged or removed when it is retrieved from the queue. Functions B and C may now be logically considered to be leaf nodes of the remaining DAG.
Functions B 404 and C 406 may now be processed. Each function may be processed by a corresponding code generator instance 310, executing concurrently in a corresponding thread. For illustrative purposes, column 508 illustrates a possible state if processing of function C completes first, at time (2). The reference count of function A 402 is decremented to one, and function C is flagged or removed. Since function A still has a non-zero reference count, its processing is not yet started.
Column 510 illustrates a state after processing of function B 404 completes, at time (3). The reference count for function A is decremented to zero, and function B is flagged or removed. Function A may now be placed on the queue and retrieved by a code generator instance. Upon completion of code generation for function A, there are no other functions to process, and the resulting contributions for each function are assembled and sorted by the assembler 312, as discussed herein.
It is to be noted that starting or completion of code generation for each function is described for illustrative purposes. In some implementations, a thread may begin processing of a function to perform initialization or code generation until a point where a code optimization decision relating to an unprocessed dependee function is to be made, and then block. For purposes of this discussion, this is considered as not yet started. Similarly, at a point of code generation when all information that may be used by a dependent function has been generated, the code generator instance may perform additional processing, though for purposes of this discussion, this may be considered as completed.
Directed graph 600 contains three cycles. Dependencies CD 620 and DC 622a form a cycle between functions C and D. Dependencies DE 624a and ED 626 form a cycle between functions D and E. Also, dependencies BD 618, DE 624a, and EB 616 form a cycle between functions B, D, and E. One aspect of the mechanisms described herein is to convert a directed graph into a directed acyclic graph (DAG) by breaking one or more dependencies. This may be performed in a deterministic way, so that the process of converting a directed graph with cycles to a DAG is deterministic. It should be noted that forming a DAG is considered deterministic when logic causes the results of multiple conversions of an equivalent input graph to form equivalent DAGs. Two DAGs are considered equivalent when they are topologically equivalent. That is, the ordering of nodes on two equivalent DAGs may differ, if the difference does not affect the topology. For example, a DAG is considered equivalent to its own mirror image. Two or more equivalent DAGS have the same dependency relationships.
The representation of table 700 to illustrate scheduling of code generation is similar to that described for
Functions B and C may now be processed concurrently, each in a respective code generator instance 310, executing in a respective thread. Note that, due to the variance in multi-threaded sequences, and also allowing that a particular system configuration may cause a single thread to be used to run two code generator instances 310 sequentially, code generation for functions B and C may be completed approximately together, or either one may occur prior to the other. In some configurations, processing one of these functions may complete before the other even begins. Three possible sequences for the completion of code generation of each function in DAG 630 are DBCEA, DBECA, and DCBEA, further illustrating the variation in the processing by a multi-threaded compilation system. Note that although function E is a root node of DAG 630, it is not necessarily processed prior to all non-root nodes.
Table 700 illustrates, in column 708, a situation in which code has been generated for function B. The string “—(P)—” indicates that function C has at least been added to the queue for processing, but has not completed processing. Function C may have been partially concurrent to function B, or completely sequential. Code generation may, at this point, proceed for functions C and E, each in a code generator instance 310, each in a respective thread. Alternatively, functions C and E may be processed sequentially. In one configuration, function E may complete processing prior to function C's completion, or even prior to the beginning of processing for function C. Column 710 illustrates a state in which code generation has been completed for functions C and E, and code generation for function A is ready to begin.
Upon completion of code generation for function A, there are no other functions to process, and the resulting contributions for each function are assembled and sorted by the assembler 312, as discussed herein.
In the example of DAG 630 discussed above, functions C and E may be processed concurrently. Moreover, either one of functions C or E may be processed prior to the other, or varying amounts of concurrency may occur. Thus, code generation for function C behaves as if function E has not yet been processed. Information obtained while processing function E is not used during code generation of function C. This enables the code generated for function C to be deterministic, such that it may be identical regardless of the sequence during a particular code generation. It may be said that a broken dependee function is hidden from a corresponding broken dependent function. (e.g. Function E is hidden from function C.) It is to be noted that if program code is modified in any of the functions A-E, and a recompilation is performed, a different DAG may result, such that the subsequently generated code is not necessarily equivalent to code generated prior to the modification.
In one embodiment, a user may specify that one or more functions, or all functions, are to be processed without inter-procedural optimization. In this embodiment, all dependencies in which the indicated functions are dependent functions may be broken, so that all such functions may be leaves of DAG, and may be ready for code generation without waiting for other functions to be processed. In a configuration in which a specification that all functions are to be processed in such a manner, in one implementation all dependencies may be broken, allowing concurrent processing of any functions. In one implementation in which all functions are specified to be processed without inter-procedural optimization, the actions of analyzing dependencies may be skipped, such that all functions may be initially placed on a processing queue.
As illustrated in
Processing may flow to block 803, where an analysis of function dependencies is performed, for the set of source functions. A dependency on a dependee function X may represent a call to function X, a reference to a variable or data object defined by function X, or another reference such that information relating to function X may be used to optimize the dependent function.
In one implementation, analysis of function dependencies may include generation of a directed acyclic graph (DAG).
Processing may flow to block 806, where the directed graph is converted to a directed acyclic graph (DAG). This may include actions of breaking one or more dependencies from the directed graph in order to remove cycles. If the directed graph generated at block 804 is already a DAG, the actions of block 806 may be skipped. The DAG may include nodes that represent functions and arrows that represent dependencies, as described herein.
Processing may flow to block 808, where each of the functions represented in the DAG is further compiled. The actions of block 808 are illustrated in more detail in
Processing may flow to block 810, where the compilation output, referred to as contributions, for the functions represented in the DAG are combined and assembled. In one implementation this may result in a single intermediate file or data object, though other implementations may generate multiple files or data objects. In one implementation, one intermediate file is created for each input source file. Processing may flow to block 812, where the contributions corresponding to the functions are sorted based on one or more keys. In one implementation, a key may be selected to generate an ordering that at least approximately matches an ordering in the input source. As described herein, at least some of the actions of sorting may be combined with the actions of assembling, or they may be performed prior to assembling.
Processing may flow to block 814, where the assembled contributions may be further processed by performing linking operations. Linking operations may include resolving memory or symbol references, relocating code, or other actions of conventional linkers. The result of block 814, and process 800, may be an application executable file or object that is deterministically produced based on the input source. If process 800 is performed multiple times on identical input sources, the application executable produced by each iteration will be identical, or at least equivalent and substantially identical, to each other application executable, regardless of differences due to concurrent compilation of some functions.
As illustrated in
By way of example, if the process 900 is performed on the functions of directed graph 400 (which is a DAG) of
Processing may flow to block 904, where a loop begins, referred to herein as loop 904. Loop 904 may iterate until all functions of the DAG have been processed, and therefore no more functions remain.
Processing within loop 904 may proceed to block 906, where the next function is retrieved from the processing queue. Within loop 904, this retrieved function is referred to as the “current” function. As discussed above, in various implementations, various mechanisms may be used to determine the next function to retrieve from the queue, when more than one function is on the queue.
Processing may flow to block 908, where the current function is compiled. Compiling the current function may include generating code, optimizing the code, producing symbol tables, debug information, exception information, or other information, referred to as contributions corresponding to the current function.
Processing may flow to block 910, where an inner loop begins, referred to herein as inner loop 910. Inner loop 910 may iterate for each function that is dependent on the current function, based on the DAG. Within inner loop 910, the dependent function that is being iterated on is referred to as the current dependent function.
Processing may flow to block 912, where the reference count for the current dependent function is decremented by one, indicating that one dependency has been removed. In one implementation, actions of accessing or decrementing the reference count may employ synchronization or locking operations to facilitate multiple threads accessing or modifying data such as the reference counts. Processing may flow to block 914, where a determination is made of whether the reference count for the current dependent function has been decremented to zero, indicating that it has no active dependencies. If this is true, the current dependent function is added to the processing queue, as discussed herein, such as with reference to block 902.
Processing may flow to block 916, which terminates inner loop 910. If there are additional functions that are dependent on the current function to iterate over, processing may flow back to the beginning of inner loop 910 to continue processing the next dependent function. If there are not additional functions to iterate over, the processing may exit inner loop 910 and flow to block 918.
In different implementations, the particular actions that are performed in thread 920 may vary to more or less than those illustrated in
In the embodiment of process 900 as illustrated in
It will be understood that each block of the flowchart illustrations of
The above specification, examples, and data provide a complete description of the manufacture and use of the composition of the invention. Since many embodiments of the invention can be made without departing from the spirit and scope of the invention, the invention resides in the claims hereinafter appended