|
|
Facet Programming Model(Last Update: April 1, 2003) 1. Functionality and FacetThe fundamental abstraction in our system is facets. Facets are software components which implement a single functionality, have a single publicly callable interface and have no residual state. A functionality can be considered a single well-defined task in an application. Essentially, functionality can be seen as a contract defining what should be done. The contract includes:
Basically, the contract defines the functionality to be achieved, but not how it should be achieved. Implementations can use different algorithms, each with different performance characteristics or resource requirements. As long as they adhere to the contract, they can be considered achieving the same functionality. Facets, are pure functional units, which are independent of the data or user interface (UI). A facet has a single publicly callable method and has no residual state, i.e. a facet does not keep any state beyond a single invocation. This implies that any call to a facet is independent of previous invocations on it. These features make them disposable – a facet can be discarded from the run-time as soon as it is used. If it is required again, another compatible facet can be brought in from the network and used again. In Sparkle, a facet consists of two parts:
2. Facet DependenciesFacet dependencies are the functionalities a particular facet depends on. In order to achieve the facet’s functionality, a facet may call upon other facets. For example, an implementation of the Gaussian Blur functionality may depend on a matrix multiplication functionality. Note that functionalities themselves have no dependencies, it is the facets which have dependencies. Different implementation of a functionality may have different dependencies. One may require two other functionalities. Another may not have any dependencies at all and achieves the whole functionality internally. 3. Facet Execution TreeSince the facets are actually composed and linked at run-time, every time certain functionality is required, it is possible to get a different facet each time. And that facet, in turn, may have different dependencies. In essence, which facets are executed only can be determined at run-time. Figure 2. Different Execution Trees of Facet x (Tree formed by all the nodes in dark)
Let’s say we have a facet x which depends on two functionalities, A and B. Each of facets i, j, k fulfill functionality A and facets p, q, r fulfill functionality B. At run-time, when we are executing facet x and it requests for functionality A, which of the facets i, j, k is brought in depends on the run-time characteristics and matching mechanism of the proxy. At one instance, it could be i. At another time it could be k. Even though i, j, k are compatible, they may have different dependencies. Again, which facets are called can only be known during execution. The pictorial representation of which facets are actually executed at run-time, is called the facet execution tree. When a user invokes a certain functionality, it spans a whole tree of facet execution. The root of the tree is called the root facet. The facets, which are under execution at a particular instant in time, are called active facets. As soon as a facet finishes execution, it becomes inactive. Inactive facets can be discarded to make room to bring in other facets. 4. ContainerFacets do not interact with the user and do not maintain any application state. However, most applications require maintenance of some sort of state and a user interface (UI). The container is thus designed to provide an application-like abstraction to the user and also for storing data and execution state. In Sparkle, every application is associated with a container. It contains a UI which interacts with the user and a set of functionalities that the application can offer. These functionalities are stored in the container as facet specifications which include funcIDs. The UI provides a means for users to access the functionalities offered by the container. When a particular functionality is required, the corresponding facet specification is sent as a request to the proxy, bringing in an appropriate facet and its dependencies, in turn starting off the execution of a whole tree of facets. The container also keeps track of state information so that we can restore execution when it moves to another device.
Figure 3. Overall Structure of the Container
5. Facet Programming Facet programming is considerably different than traditional object-oriented programming (OOP). Facet-based programming (FBP) has as its foundation the separation of functionality and data, whereas object-oriented programming favors putting the two together. In OOP, programmers define not only the data structures of data types, but also the operations that can be applied to a data structure. FBP, on the other hand, is focused on the application logic. Programmers define what functionalities an application provides, and these functionalities become facets. The fact that a facet cannot maintain any state must be kept in mind when developing a facet. A programmer cannot assume that a facet will continue to “live” once the execution of the facet has finished, i.e. any changes to static variables will be lost. Thus, every invocation must depend on the input arguments and the data in the storage area solely. A facet is invoked by filling in a FacetRequest with specifications, initializing a Facet object and calling execute on it. In the case that a root facet is called, there is no need to fill in the facet specification, since that is already included in the application container.
Figure 4. Facet Based Programming API 6. The Design of the Client System
Figure 5. Overall Structure of the Client System
The Sparkle client system internally is made up of seven entities, each of which have dedicated responsibilities. These are:
7. Interactions with the Intelligent Proxy ServerThe proxies possess the overall view – they know what facets are available in the servers and what the requirements of the device are. The proxies receive facet specification from clients. They match the requirements of the client with the properties of the available facets and return a suitable facet to the clients. The intelligence of the proxy server lies in its solutions for facet matching and pre-fetching, which take facet version, code size, user’s preference and facet’s runtime resource consumption into consideration. For the same functionality to be carried out in the same device, different run-time execution contexts result in different facets being selected. Each of these facets has a different implementation and may require different functionalities to help fulfill their own functionality. The number of possible facet execution paths grows with the number of candidate facets that satisfy a functionality dependency. The multi-level facet calling dependency further enlarges the search space. In order to decide which facet to be chosen for execution, we need to predict the resource usage for all possible execution paths associated with each candidate facet. Since used facets can be thrown away, the minimum resource consumption is mainly contributed by active facets and their associated working memory. The problem can be generalized as finding the critical path in a task graph, where the critical path in our problem is the total resource usage of a facet execution path that causes the largest resource consumption. Some range matching and subset matching techniques have been used to minimize the search space or to realize the trade-offs of time-space efficiency. |