Design and Partial Evaluation of Meta-objects for a Concurrent ...

10 downloads 28416 Views 289KB Size Report
Concurrent objects in practical parallel and distributed applications tend to have compli- ... a customized meta-object, the application programmer can use a new ...
|revised version will appear in ECOOP'98 proceedings|

Design and Partial Evaluation of Meta-objects for a Concurrent Re ective Language Hidehiko MASUHARA

y

3

Akinori YONEZAWA

[email protected]

[email protected]

Abstract

In concurrent object-oriented languages, customizable meta-objects are powerful abstraction for extending and optimizing crucial implementation mechanisms such as method dispatch and mutual exclusion. However, interpretive execution of meta-objects causes severe performance penalty. Our previous study shows that applying partial evaluation to meta-interpreters is useful for alleviating the problem, but partial evaluation of existing meta-objects virtually fails, because they are concurrent objects and designed as state-transition machines. This paper proposes a new meta-object design for our re ective language ABCL/R3, which can be e ectively optimized using partial evaulation. The crux of the meta-object design is separation of state-related operations, which is realized by using the reader/writer methods in our concurrent object-oriented language Schematic. Our benchmarks showed that a non-trivial program with partially evaluated meta-objects runs (1) 6.8 times faster than the one with merely compiled meta-objects, and (2) only 2.2 times slower than the directly compiled program without meta-objects, which is supposed to have the best performance. In addition, a program that uses a customized meta-object for guarded method invocation runs as ecient as a directly compiled program that implements the same functionality with application-level methods. Keywords: concurrent object-oriented language, re ection, meta-objects, implemenation techniques, partial evaluation. 1

Introduction

1.1 Re ection in Parallel/Distributed Programs Concurrent objects in practical parallel and distributed applications tend to have complicated structures for achieving eciency, reliability, portability, and re-usability. To alleviate the complexity, number of language mechanisms|inheritance, transaction, object migration, etc.|are proposed. For example, guarded method invocation, which conditionally accepts invocation requests, is useful to describe objects like bounded bu ers. However, such mechanisms are not always implemented in many languages, because (1) some mechanisms are incompatible each other; (2) supporting a new mechanism could slow down the language even when the mechanism is not used in a program; and (3) implementation of a new mechanism in the language requires a tremendous amount of e ort of language implementors. A di erent, possibly better approach is language extension via meta-objects. By installing a customized meta-object, the application programmer can use a new language mechanism as if it is built-in. Advanced programmers may even develop their own meta-objects to meet their requirements. This approach is also bene cial to the language implementors; they 3 Department of Graphics and Computer Science, Graduate School of Arts and Sciences, University of Tokyo y Department of Information Science, University of Tokyo

1

can devote themselves to implementation of simple core languages by separating hard-to-beimplemented mechanisms as extensions. Based on those observations, we are developing a re ective object-oriented concurrent language ABCL/R3, in which we provide such extensibility via computational re ection [Mae87, Smi84]. In ABCL/R3, meta-objects provide abstraction with which the user can extend or modify crucial mechanisms of objects, including method invocation request, method dispatch, state management, and mutual exclusion. In addition, meta-interpreters provide abstraction to customize syntax and semantics of bodies of methods and functions. Re ective annotations can be de ned as programming directives in base-level programs, whose interpretations can also be modi ed. The details of the language features of ABCL/R3 have been presented elsewhere[MMAY95, MMY96]. In this paper, we discuss the meta-object design of ABCL/R3, especially from the viewpoint of ecient implementation.

1.2 Implementation Techniques of Meta-objects Despite its extensibility, customizable meta-objects have a problem of eciency. Assume that a meta-object implements a customized method dispatch algorithm. In a naive implementation, the method dispatch for the respective base-level object is achieved by a number of method invocations at the meta-level; i.e., interpretive execution. Moreover, the customization makes the application of important optimization techniques such as inlining[CUL89], because it could change the dispatching rules on which the optimizers rely. As a result, existence of meta-objects easily slow down the language more than 10 times. There have been several studies on this problem, mostly as implementation techniques of re ective languages:  Some parts of the meta-system are not subject to meta-level modi cation via rei cation.

For example, in the re ective language Open C++ version 1[CM93], only message passing, object creation, and instance variable accesses can be rei ed. This not only restricts user programmability, but also makes the language model unclear, in that much of the meta-level functionalities are hidden inside `black-boxes,' and the programmer may not obtain a clear view of how his meta-level programming and the black boxes will interact. Another example would be JDK 1.1, which o ers \re ection" API[Sun97]. It supports only introspective operations; they can be eciently implemented, but give few extensibility at the same time.

 The system embodies a set of ad-hoc optimizations transparent to the user. For ex-

ample, our previous work ABCL/R2[MMWY92] assumes that most objects will not be customized, and thus could be mostly compiled, and switched to general interpreted execution upon user customization. Its e ectiveness is limited to, however, cases where optimization is possible, and interpretation overhead heavily a ects the overall performance otherwise.

 The compiler inspects behavior of a meta-level program with respect to a base-level

program, and removes interpretation using techniques like partial evaluation[JGS93]. This approach is more systematic than the above two approaches, but requires intensive analysis of the meta-level program that may include user customization. We will further discuss this approach below.

1.3 Optimization Using Partial Evaluation The execution of meta-objects is interpretive in that they manipulate a base-level program and its run-time data, and the computation speci ed in the base-level program is embodied as an indirect result of meta-level computation. However, once the base-level program is given, 2

most computation that depends only on the base-level program can be performed in advance to the program execution, by generating a specialized program which contains computation that depends on run-time data extracted from the meta-level computation. This process is often referred as partial evaluation [JGS93] of the meta-level program with respect to the baselevel program, or the \Futamura Projections"[Fut71]. Our previous studies successfully apply partial evaluation to the meta-interpreters in re ective languages[MMAY95, AMMY95]. In theory, meta-objects could also be optimized by partial evaluation. However, it virtually fails because: (1) the design of meta-objects in existing re ective languages is not suitable for partial evaluation, and (2) there are few partial evaluators that can deal with concurrent objects. We therefore re-design meta-objects with consideration to application of partial evaluation, and will show an optimization framework for the resulting meta-objects. We therefore tackle optimization of meta-objects from their design. The rest of the paper is organized as follows: In Section 2, we discuss why reasoning about the meta-objects is dicult by reviewing an existing meta-object design. In Section 3, our proposed meta-object design is presented, and its optimization technique using partial evaluation in Section 4. Section 5 gives performance measurement of the optimized objects. Section 6 discusses other ecient implementation techniques for meta-objects. Section 7 concludes the paper. 2

Problems of Existing Meta-object Design

Many concurrent object-oriented languages have mutual exclusion mechanisms to assure consistency. A conservative, commonly found approach is to mutually exclude all method executions per-object basis, where the programmers can write accesses to instance variables in a method with less concern about access con icts. On designing a meta-object, the mutual exclusion mechanism plays a key role: (1) The mutual exclusion mechanism of base-level objects should be implemented in extensible ways at the meta-level. (2) The implementation|written as methods of meta-objects|is executed under some mutual exclusion mechanism, which is usually the same as the one in the default base-level. In previous re ective languages, a meta-object is de ned as a state transition machine to meet the above requirements. For example, Figure 1 is a simpli ed de nition1 of the default meta-object in the language ABCL/R[WY88], which is de ned as a state transition machine as illustrated in Figure 2. A method invocation on a base-level object is represented by an invocation of the method receive!2 on its meta-object. In receive!, the message (an object that contains the method name and arguments) is immediately put into the message queue (queue), so that it will be eventually processed. If the object is not processing any methods (i.e., mode is 'dormant), it changes mode to 'active and calls the method accept!. The method accept! gets one message from queue, and lets the evaluator execute the matching method to the message. The evaluator recursively interprets expressions of the method. When the evaluator reaches the end of the base-level method, it invokes the method finish! of the meta-object. The method finish! examines queue for any pending messages, which are received during the evaluation. If queue is empty, it changes mode to 'dormant. Otherwise, it invokes accept! again for further execution. When we apply partial evaluation to this meta-object de nition with respect to a certain base-level object, the result is far from satisfactory. The reasons are follows:  Since the meta-object is de ned as a state transition machine, its behavior cannot be

determined without static information on some key instance variables such as mode

1 The de nition here is given in the Schematic's syntax[TY96], which is di erent from the original version for the sake of uniformity. 2 The exclamation mark in the method name conventionally indicates that the method may change the object's state.

3

;;; Class de nition

(define-class metaobj () mode queue state methods evaluator) ; instance variables

;;; Method de nition for class metaobj

(define-method! metaobj (receive! self message) ;; Here, self is bound to the metaobject itself. (put! queue message) (if (eq? mode 'dormant) ; If it is dormant, the received (begin (set! mode 'active) ; message is immediately accepted. (future (accept! self)))))

;;; method dispatch

(define-method! metaobj (accept! self) (let* ((mes (get! queue)) ; Get a message from the queue. (m (find methods mes)) ; method lookup (env (extend state (args mes)))); creation of an evaluation env. (future (eval evaluator (exps m) env self)))); evaluation

;;; end of method execution

(define-method! metaobj (finish! self) (if (empty? queue) ; Check the queue for pending messages. (set! mode 'dormant) ; If none, turn into the dormant mode. (future (accept! self)))) ; Otherwise, accept one of them.

;;; meta-interpreter

(define-method! metaobj (eval self exp env owner) It evaluates exp under env. When nished, it invokes finish! of owner. )

The expression (future (m r e1 : : : en )) asynchronously invokes method m of object r with parameters e1 : : : en . It is asynchronous in that the sender continues subsequent computation without waiting for the return value. On the other hand, expression (m r e1 : : : en ) is synchronous invocation; the sender waits for the return value.

Figure 1: De nition of ABCL/R meta-object

receive! true, queue

receive!

true, accept dormant active finish! message |queue|=0, none state state condition, action

finish! |queue|>0, accept

Figure 2: State Transition Diagram of ABCL/R Meta-object

4

and queue. For example, if the return value of (get! queue) in the method accept! were unknown (dynamic) at the specialization time, specialization of method dispatch ((find methods m)) and interpretation of the method body ((eval evaluator exp env self)) would be left unspecialized. This means that most interpretive computation cannot be eliminated by partial evaluation.  Information that should be \known" (static) to the partial evaluator is transferred via

instance variables between consecutive method invocations. Such information cannot be available at the receiver's side without extensive analysis on data structures. For example, the value of (get! queue) in accept!, which would be that of message in receive!, is crucial for specialization, but it requires analysis of queue. This requirement sometimes become overwhelming because queue might be an user-de ned object.

 The key instance variables are mutable; i.e., their values are changed during execution.

The execution model of the meta-objects|ABCM[Yon90] in this case|however speci es that method invocations on an object will be processed in the FIFO order. We thus have to anticipate that the execution two consecutive methods may be interleaved; i.e., it is safe to assume that those variables may be changed in between method invocations. For example, we should not assume that the variable queue at the beginning of the method accept! followed by receive! remain unchanged since the method is invoked in receive!. Though there are partial evaluators that can deal with mutable variables, they regard a mutable variable as dynamic (unknown) unless they could statically determine all update operations to the variable[And94, AMY97].

Due to the above reasons, a partial evaluator conservatively regards most variables as \dynamic." Without much of \known" information to e ectively specialize the meta-level computation, almost all the computation in the meta-object still remains in the result of the partial evaluation. 3

A New Meta-object Design

We propose a new meta-object design for a re ective concurrent object-oriented language ABCL/R3[MMAY95, MMY96], which can be e ectively optimized by partial evaluation. The key idea is to separate state-related operations from the other operations, using reader and writer methods of Schematic. In this section, we rst explain the reader/writer methods, and then show the proposed meta-object design.

3.1 Reader/Writer Methods Schematic[TY96, OTY97] is a concurrent object-oriented language based on Scheme. It has concurrency primitives such as future and touch , and class-based objects. Here, we mainly describe the reader/writer methods in Schematic, which play a key role in our meta-object design. The construct define-method! de nes a writer method , which can modify values of instance variables in an object. When a form \(become rexp :v1 e1 :v2 e2 ...)" in a writer method is evaluated, expressions ei are rst evaluated. Then the values of variables vi are changed to the respective evaluated values all at once. Finally, the result expression (rexp ) is evaluated. Invocations of writer methods on an object are mutually excluded. (Precisely, result expressions in become forms are out of the mutual exclusion.) The construct define-method de nes a reader method , which cannot modify instance variables. The reader methods are not governed by the mutual exclusion mechanism; it can be executed concurrently even with a writer method on the same object. During execution of a reader method, the instance variables are bound to the values extracted from the object's status at the beginning of the method. Even when a writer method executes become to 5

;;; Class de nition

(define-class metaobj () lock state-variables state-values methods evaluator)

;;; Reception of a message

(define-method metaobj (receive self message) (if (writer? (selector message)) ; check the type of message (accept-W self message) ; for a writer method (accept self message 'dummy))) ; for a reader method

;;; Processing for a writer method

(define-method metaobj (accept-W self message) (let ((c (make-channel))) ; channel for receiving updated state (acquire! lock) ; mutual exclusion begins (let ((result (accept self messages c))) (cell-set! state-values (touch c)) ; update instance variables (release! lock) ; end of mutual exclusion result)))

;;; Method lookup and invocation

(define-method metaobj (accept self message update-channel) (let* ((m (find methods message)) ; method lookup (env (make-env self (method-formals m) message))) (future (eval evaluator (method-body m) env update-channel))))

;;; Meta-interpreter

(define-method evaluator (eval self exp env update-channel) It evaluates exp under env. When a become form is evaluated, it creates a vector of new updated instance variables, and sends it to update-channel. )

The primitive make-channel creates an empty channel, which is a communication medium among concurrently running threads. A thread sends a value to a channel c by executing (reply value c), and receives from c by (touch c).

Figure 3: Our New Meta-object Design modify some of the instance variables, reader methods that have started their execution before the become operation do not observe the e ect of modi cation.

3.2 Proposed Meta-object Design We propose a new meta-object design aiming to solve the problems discussed in Section 2. The outline of the design is shown in Figure 3, in which we exploit the reader/writer methods of Schematic. Compared to the previous meta-object design (Figure 1), our design has the characteristics as follows:  Behavior of the meta-object is principally de ned in the reader methods. Operations

that deal with mutable data are separately de ned as writer methods to external objects. For example, values of instance variables are mutable are packed in the mutable vector object state-values, and accesses to state-values are realized through the writer methods cell-set! and cell-ref.

 The meta-object straightforwardly processes each method invocation request, and re-

alizes mutual exclusion by blocking operations(e.g., acquire! and release!). As a result, the meta-object is no longer a state-transition machine. The ability of multiple method invocations on the same object makes such a de nition style possible. Without reader methods, blocking operations would easily lead to deadlock. 6

 For mutual exclusion, the instance variable lock is de ned in place of mode and queue.

By default, lock is a simple semaphore that has the operations acquire! and release!. The user can replace lock with an arbitrary object, such as a FIFO queue and a priority queue, via the meta-level programming.

These characteristics solve the problems of application of partial evaluation in Section 2: (1) Under the execution model of Schematic[TY96], it is safe to assume that consecutive reader method invocations are not interrupted by other activities; we therefore can use most of partial evaluation techniques for sequential languages, by regarding the reader methods as functions. (2) Since the \known" (static) information is propagated through the arguments of the method invocations, the partial evaluators easily use such information for specialization. (3) The mutual exclusion mechanism, which is implemented by the blocking operations, gets rid of dynamic branches (conditionals with dynamic predicates), which cause a termination detection problem during specialization. In practice, we have successfully partially evaluated the meta-object, as we will present in Figure 7 in Section 4. How the methods in Figure 3 handle messages sent to the base-level object is explained as follows: receive: The method receive simply proceeds to invoke methods accept-W or accept,

depending on the type of the base-level method that is to be invoked.

accept-W: The method accept-W wraps the method accept in the code for mutual exclusion

and update of base-level instance variables. It rst locks the object, and then calls

accept method with a channel c. It then waits for a vector of updated instance variables|which is sent by the become form in the base-level method|on c by the touch form, and updates state-values with the received value. Finally, it unlocks the object, and returns the result of the method. Note that accept-W does not modify

instance variables itself.

accept: The method accept merely looks up a method for a given message, and lets the evaluator execute it. Make-env is an auxiliary method to create an evaluation environment. It rst extracts a vector of instance variable values by executing (cell-ref state-values), and then creates an association list that maps each of the instance

variable names to the extracted value, and each formal parameter name of the method to the parameter value in the message.

eval: The method eval of class evaluator and its auxiliary methods embody a meta-

circular interpreter, which is similar to the traditional Lisp meta-circular interpreter. When it encounters a become form, it creates a vector of the updated instance variables, and then sends the vector to update-channel.

4

Optimization using Partial Evaluation

In our proposed meta-objects, most operations are de ned in the reader methods, and a few invocations to external objects are used for mutual exclusion and state modi cation. As we stated earlier, the meta-objects can be regarded mostly as a functional program with I/O type side-e ects, from the viewpoint of partial evaluation. In this section we show an optimization framework of our meta-objects using partial evaluation. The foremost problem to apply partial evaluation is that there are no appropriate partial evaluators for our purpose because the meta-object is written in a concurrent object-oriented language. Though there are studies on partial evaluators for concurrent languages[HMTY95, MG97, GM97], they focus on concurrency, and pay little e ort to support crucial features of sequential languages such as function closures and data structures. 7

meta-level ABCL/R3

base-level

classes methods functions

classes methods functions

records & functions

Scheme

preprocessing

(d)

(a)

specialization point functions

P.E. (e)

(c) (f,g)

result expressions

postprocessing

(h)

Schematic

classes & constructors

(b)

methods

partial evaluation

functions

Figure 4: Overview of Our Optimization Framework ;;; 2d-point

(define-class point () x y)

;;; returns the distance from the origin|a reader method

(define-method point (distance self) (sqrt (+ (square x) (square y))))

;;; moves a point|a writer method

(define-method! point (move! self dx dy) (become #t :x (+ x dx) :y (+ y dy)))

Figure 5: Example Base-level Program Our solution is to (1) translate meta-objects into a sequential program, and (2) use a partial evaluator for a sequential language. Partial evaluation is applied for each base-level method invocation; i.e., the specialization point is base-level method invocation. Since the methods of meta-objects exhibit almost sequential behavior, the sequential partial evaluator can e ectively optimize them. Concurrency in the meta-objects will be residualized as applications to primitives. Another problem is compatibility with other objects. The resulting object of our optimization should support meta-level operations that is de ned in the original meta-object. At the same time, the object should behave like a base-level object so that it can be used with other base-level objects. To satisfy these two requirements, our framework generates an object de nition which is a combination of the base- and meta-level objects in a single level. The object has the same methods to the ones in the original base-level de nition, and the body of each method is a specialized code of the meta-object. Figure 4 shows the overview of our optimization framework. It consists of three steps: (1) translation from ABCL/R3 to Scheme, (2) partial evaluation, and (3) translation from Scheme to Schematic. In the following subsections, we explain the details of each step using an example base-level program (Figure 5) and the default meta-object metaobj (Figure 3).

4.1 Pre-processing Meta-object de nitions are translated into a Scheme program, so that it can be processed by a Scheme partial evaluator. (Figure 4(a)) A meta-level object is converted into a record3 whose elds are its class name and values of instance variables. A reader method is converted into a dispatching function and a class-speci c function. The former examines the class-name 3 Since

our partial evaluator does not natively support records, we further translate into cons-cells.

8

(define (specalization-point-move!-point state-values lock dx dy) (let ((mobj (metaobject 'point '((distance (self) ...) ...) '(x y) state-values lock (make-evaluator))) (message (message 'move! (list dx dy)))) (receive mobj message)))

Figure 6: Specialization Point Function for Method move! of Class point. eld in the receiver, and calls a matching class-speci c function. Invocations on writer methods at the meta-level methods should not be performed at the partial evaluation time, as they will modify the state of objects. Therefore, the writer methods are not passed to the partial evaluators, and simply copied into the resulting Schematic program. (Figure 4(b)) No translations are needed for the base-level de nitions since they are used as data to the meta-level program. Functions, however, are simply copied to the resulting Schematic program. (Figure 4(c))

4.2 Partial Evaluation We partially evaluate the meta-level program for each base-level method invocation. For example, given the base-level program like Figure 5, the meta-level computation that corresponds to the following base-level method invocation will be processed: (move!

p dx dy

)

where p = pointfx = x; y = y g: (Here, the variables written in italic fonts (e.g., dx; dy; x; and y ) are dynamic data. The variable p is a partially static data; it is known as an object of class point, but values of instance variables x and y are dynamic (unknown).) The corresponding meta-level computation is the following expression: (receive mobj message )

where

mobj

= metaobjfclass = 'point; methods = '((distance (self) ...) ...), state-vars = '(x y); state-values = s; lock = l; evaluator = (make-evaluator)g; message = messagefselector = 'move!; arguments = (list dx dy )g: To partially evaluate the meta-level computation like the above one, we generate a specialization point function for each base-level method. (Figure 4(d)) The function takes a vector of instance variables, lock, and parameters for the method as its arguments. When called, it creates mobj and message , and invokes the method receive on mobj . (Figure 6) The partial evaluator specializes the function assuming that all parameters to the function are dynamic. We use an online partial evaluator for Scheme[AMY97]. (Figure 4(e)) It specializes not only the methods of metaobj, but also those of evaluator in practice4 . The compilation techniques of the meta-interpreter is described in the other literature[MMAY95]. 4 For the convenience of benchmarks, instead of a real meta-interpter, we used a fake evaluator that directly executes the body of methods. This will be discussed in Section 5.

9

4.3 Post-processing The nal step is to re-translate the results of partial evaluation (in Scheme) into concurrent objects (in Schematic). Here, we generate class declarations, constructor functions, and methods, as are shown in Figure 7.  For each combination of base- and meta-level classes, a specialized class is de ned. (Fig-

ure 4(f)) Since the class is a specialized version of the meta-level class, it has the same instance variables as the original meta-object. (E.g., the class metaobject**point in Figure 7.)

 A function that mimics the base-level constructor is de ned for each specialized class. (Fig-

ure 4(g)) For example, the function point in Figure 7 is such a constructor which creates an object belonging to class metaobject**point with proper initial valeus.

 Methods of the specialized classes are de ned. (Figure 4(h)) The name of each method

is the same as the original base-level methods. (The method distance and move! of class metaobject**point in Figure 7 are such examples.) Therfore, the specialized object has the same interface as the original base-level program. The body of the method is the result of partial evaluation. Note that since the generated methods are specialized versions of receive of the meta-object, they should be de ned as reader methods, regardless of the type of the corresponding base-level method.

When a meta-object is specialized with respect to a reader method, the optimized method has the essentially same de nition to the original base-level method, except the indirect accesses to the instance variables (cf. The method distance in Figure 7). On the other hand, when it is specialized with respect to a writer method, the optimized method appears to contain extra operations. Although some are the same operations as the ones performed inside of a writer method in Schematic, others are amenable to further optimization. For example, the newly created vector of instance variables g0 is handed over via reply and touch operations in the same thread, because those operations are regareded as mere \unknown" functions in our current partial evaluator. This problem could be overcome by using partial evaluators for concurrent languages, or applying static analysis for concurrent programs[IK97, KPT96, KNY95] to the resulting code. 5

Performance Measurement

To measure the eciency of our partially evaluated meta-objects, we executed benchmark programs in the following three ways: PE: The default meta-object is partially evaluated with respect to each benchmark program,

and the generated code is further compiled by Schematic. This will show the performance of our optimization framework.

Interpreted: The default meta-object is directly compiled by Schematic, and the compiled

code interprets the benchmark programs. This will show the performance of naively implemented meta-objects.

Non-re ective: The benchmark programs are directly compiled by Schematic. This will show

the performance of non-re ective languages.

All programs are executed on Sun UltraEnterprise 4000 with 14 UltraSparc processors5 at 167MHz and 1.2GB memory, running SunOS 5.5.1. The di erences between PE and Interpreted (Figure 9) show the amount of speedup gained by partial evaluation, while the di erences between PE and Non-re ective show the residual overheads |the overheads that the partial evaluator fails to eliminate (Figure 10). 5 Though

we used a multi-processor machine, the programs are executed on a single processor execution.

10

;;; a mixed class of metaobject and point

(define-class metaobject**point () class methods state-vars state-values lock evaluator)

;;; constructor

(define (point x y) (metaobject**point (quote *metaobject*) (quote *methods*) (quote (x y)) (make-cell (vector x y)) (make-lock) (quote *evaluator*)))

;;; reader method

(define-method metaobject**point (distance self) (begin (let* ((values0 (read-cell state-values)) (x0 (vector-ref values0 0)) (y0 (vector-ref values0 1)) (g0 (square x0)) (g1 (square y0))) (sqrt (+ g0 g1)))))

;;; writer method

(define-method metaobject**point (move! self dx dy) (begin (acquire! lock) (let* ((state-update-channel0 (make-channel)) (values0 (read-cell state-values)) (x0 (vector-ref values0 0)) (y0 (vector-ref values0 1)) (g0 (vector (+ x0 dx) (+ y0 dy)))) (reply g0 state-update-channel0) (let ((new-state0 (touch state-update-channel0))) (update-cell! state-values new-state0) (release! lock) #t))))

Figure 7: Result of Optimization (The underlined expressions come from the base-level method.) In order to examine on the overheads solely caused by the meta-objects, body expressions in PE and Interpreted are executed without meta-interpreters. For example, when a base-level program has an expression \(distance p)," then a meta-object looks up distance in its method table and extracts instance variables from p. However, the method body \(sqrt (+ (square x) (square y)))" should be directly executed. To do this, we generate a fake evaluator for each base-level class, as is shown in Figure 8. Without fake evaluators, interpretive execution of method bodies would overwhelm the whole execution time in Interpreted. The fake evaluators are also useful to skip over the partial evaluation of meta-interpreters, when a base-level object uses only the default meta-interpreter.

5.1 Base-level Applications The following three programs are executed as the base-level applications: and Null Writers: Elapsed time for a single method invocation is measured by repeatedly calling a null method on an object. We tested objects with di erent numbers of instance variables (i), and methods with di erent numbers of arguments (j ). The average time over some parameter combinations (i 2 f0; 5; 10g; j 2 f1; 5; 10g) are shown as a representative result.

Null Readers

11

;;; Class de nition

(define-class evaluator**point ())

;;; The method called by the meta-object.

(define-method evaluator**point (eval-begin self method-name exp env) (cond ((eq? method-name 'distance) ; for method distance (let ((x (lookup 'x env)) (y (lookup 'y env))) (sqrt (+ (square x) (square y))))) ((eq? method-name 'move!) ; for method move! (let ((x (lookup 'x env)) (y (lookup 'y env)) (dx (lookup 'dx env)) (dy (lookup 'dy env))) (let ((new-values (vector (+ x dx) (+ y dy)))) (update self new-values))))))

Each clause of the cond form in eval-begin corresponds to the method of the baselevel class point. A clause is selected by the argument method-name, and executed after extracting the base-level arguments and instance variables from the environment. A become form in the original program is converted into an invocation of the update method to the meta-object with a vector of updated instance variables.

Figure 8: \Fake" Evaluator for point Become: Elapsed time for an invocation of writer methods which update instance variables is measured by repeatedly calling a method that immediately performs become. We

tested with the di erent numbers of updated variables (k ). The average time over the following parameter combinations is used as the representative result: i = 10; j = 1; k 2 f1; 5; 10g. 6

Richards: The Richards benchmark is an operating system simulation, which is used as a

non-trivial program in several object-oriented languages[CUL89]. Since it uses both functions and methods, we could know how the eciency of the meta-objects a ects overall execution speed in realistic applications.

The results are summarized in Figure 9 and 10. As Figure 9 shows, the programs in PE are more than 4 times faster than the ones in Interpreted. This improvement is signi cant even in realistic applications such as Richards, whose factor is 6.8. Figure 10 shows the residual overheads in PE. The programs in PE are still slower than the ones in Non-re ective by the factor of 1.1{3.1. These overheads mainly come from limitations of current partial evaluators, as stated in Section 4. In fact, when we further optimized the partially evaluated meta-objects for Become by hand|resolving obvious channel communications, etc.|the average residual overheads are reduced to 1.4.

5.2 Performance of Customized Meta-objects The above benchmark programs are executed under the default meta-objects. More practical interest is eciency of customized meta-objects. The next benchmark program is a boundedbu er that uses the guarded method invocation mechanism, which is implemented by a customized meta-object. Since the guarded methods are not directly supported in Schematic, we simulate them by user-level programming, in which objects are programmed to check the guard conditions and spontaneously suspend/continue their invocation requests. Figure 11 shows the elapsed time for a program that writes and reads 1,000 integers with a bounded bu er whose size is 10. The bu er for PE shows almost the same eciency to the one for Non-re ective. This result could be understood as the cancellation of the 6 The values of i and j are the worst case in Null Writers. 12

Richards

6.81 5.98

Become Null Writers

4.64

Null Readers

33.91

0.00

2.00

4.00

6.00

8.00

Figure 9: Performance Improvement by Partial Evaluation (The length of each bar denotes the execution time in Interpreted relative to PE.)

Richards Become Null Writers Null Readers 0.00

1.00

2.00

3.00

Figure 10: Residual Overheads in Partially Evaluated Meta-objects (The length of each bar denotes the execution time in PE relative to Non-re ective.)

Interpreted

4.46

PE

3.94

Non-reflective

3.97

0.00

1.00

2.00 3.00 4.00 elapsed time (sec)

5.00

Figure 11: Performance of Bounded Bu er with Guarded Methods

13

residual overheads in PE by frequent method invocations in Non-re ective. In Non-re ective, we have to de ne three methods for each guarded method. On the other hand, the partial evaluation successfully inlines the operations for guarded methods, which are de ned as the meta-object's methods in PE. The partially evaluated meta-objects are approximately 10 percent faster than the interpreted ones (Interpreted). This improvement is less signi cant than the previous benchmarks. We conjecture that the three programs require a large number of context switches in common, which are expensive in the current Schematic implementation. As a result, the time spent for the context switching becomes dominant, and makes the di erences among three programs relatively smaller. 6

Related Work

In CLOS Meta-Object Protocols (MOP), meta-level methods are split into functional and procedural ones for caching (or memoization )[KdRB91, KR90]. This splitting approach has similarity to our meta-object design in principle. However, memoization technique requires more careful protocol design because the unit of specialization is function; thus the \functional" methods cannot include operations that touch dynamic data. On the other hand, such operations can be written in our reader methods, since the partial evaluator automatically residualizes them. Compile-time MOP is the di erent approach to ecient re ective systems[Chi95, IHS+ 96]. In compile-time MOPs, eciency is guaranteed by only allowing the meta-level computation at the compile-time. This means that the changes to the run-time behavior of the base-level program should be achieved by writing translation rules that converts the program into the one containing the expected behavior. This could be a burdensome task if the modi cation involves run-time representation of an object, because no run-time meta-objects are available in compile-time MOPs. 7

Conclusion

We have given a meta-object design for our re ective language ABCL/R3 with an optimization framework using partial evaluation. In the meta-object's description, state-related operations are separated from the rest, which enables e ective partial evaluation. The optimization framework translates the meta-objects and their reader methods into records and functions in Scheme, and then applies a Scheme partial evaluator. The optimized code is a combination of the base- and meta-level programs, where most interpretive operations at the meta-level, such as method dispatch and environment manipulation, are removed. The eciency of our optimization is shown by benchmarks where the partially evaluated objects run signi cantly faster than the interpretive meta-objects. Moreover, a program with customized meta-objects runs as eciently as an equivalent non-re ective program, thanks to the partial evaluation. Acknowledgments

The earlier version of ABCL/R3 was designed in collaboration with Satoshi Matsuoka. We would like to express our thanks to him. We also would like to thank Kenjiro Taura, who is the principal designer and implementor of the Schematic language. Kenichi Asai o ered us a partial evaluator for Scheme. Ken Wakita helped us to run the Schematic compiler on our system.

14

References

[ACM97]

ACM. Proceedings of Partial Evaluation and Semantics-Based Program Manipulation (PEPM'97), Amsterdam, June 1997.

[AMMY95] Kenichi Asai, Hidehiko Masuhara, Satoshi Matsuoka, and Akinori Yonezawa. Partial evaluation as a compiler for re ective languages. Technical Report 95{ 10, Department of Information Science, the University of Tokyo, December 1995. [AMY97]

Kenichi Asai, Hidehiko Masuhara, and Akinori Yonezawa. Partial evaluation of call-by-value lambda-calculus with side-e ects. In Proceedings of Partial Evaluation and Semantics-Based Program Manipulation (PEPM'97) [ACM97], pages 12{21.

[And94]

Lars Ole Andersen. Program Analysis and Specialization for the C Programming Language. PhD thesis, DIKU, University of Copenhagen, May 1994. (DIKU report 94/19).

[Chi95]

Shigeru Chiba. A metaobject protocol for C++. In Proceedings of OOPSLA'95 (SIGPLAN Notices Vol.30, No.10), pages 285{299, Austin, TX, 1995. ACM.

[CM93]

Shigeru Chiba and Takashi Masuda. Designing an extensible distributed language with a meta-level architecture. In Proceedings of European Conference on Object-Oriented Programming (ECOOP), 1993. LNCS 707.

[CUL89]

Craig Chambers, David Ungar, and Elgin Lee. An ecient implementation of self, a dynamically-type object-oriented language based on prototypes. In Pro-

ceedings of Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), pages 49{70, New Orleans, LA, October 1989. ACM.

[Fut71]

Yoshihiko Futamura. Partial evaluation of computation process|an approach to a compiler-compiler. Systems, Computers, Controls, 2(5):45{50, 1971.

[GM97]

Marc Gengler and Matthieu Martel. Self-applicable partial evaluation for the pi-calculus. In Proceedings of Partial Evaluation and Semantics-Based Program Manipulation (PEPM'97) [ACM97].

[HMTY95] Haruo Hosoya, Hidehiko Masuhara, Kenjiro Taura, and Akinori Yonezawa. Partial evaluation for a concurrent language based on processes and channels. SWoPP Beppu '95 (IPSJ SIG Notes 95-PRO-2), 95(82):73{80, August 1995. [IHS+ 96]

Yutaka Ishikawa, Atsushi Hori, Mitsuhisa Sato, Motohiko Matsuda, Jorg Nolte, Hiroshi Tezuka, Hiroki Konaka, Munenori Maeda, and Kazuto Kubota. Design and implementation of metalevel architecture in C++: MPC++ approach. In Re ection Symposium'96, San Francisco, CA, April 1996.

[IK97]

Atsushi Igarashi and Naoki Kobayashi. Type-based analysis of usage of communication channels for concurrent programming languages. In Proceedings of International Static Analysis Symposium (SAS'97), volume 1302 of Lecture Notes in Computer Science, pages 187{201. Springer-Verlag, 1997.

[JGS93]

Neil D. Jones, Carsten K. Gomard, and Peter Sestoft. Partial Evaluation and Automatic Program Generation. Prentice Hall, 1993.

[KdRB91]

Gregor Kiczales, Jim des Rivieres, and Daniel G. Bobrow. The Art of the Metaobject Protocol. MIT Press, Cambridge, MA, 1991.

15

[KNY95]

Naoki Kobayashi, Motoki Nakade, and Akinori Yonezawa. Static analysis of communication for asynchronous concurrent programming languages. In Second International Static Analysis Symposium (SAS'95), volume 983 of Lecture Notes in Computer Science, pages 225{242. Springer-Verlag, 1995.

[KPT96]

Naoki Kobayashi, Benjamin C. Pierce, and David N. Turner. Linearity and the pi-calculus. In Conference record of Symposium on Principles of Programming Languages, pages 358{371, January 1996.

[KR90]

Gregor Kiczales and Luis Rodriguez. Ecient method dispatch in PCL. In Proceedings of Conference on LISP and Functional Programming, pages 99{105, 1990.

[Mae87]

Pattie Maes. Concepts and experiments in computational re ection. In Proceedings of Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), pages 147{155. ACM, October 1987.

[MG97]

Mihnea Marinescu and Benjamin Goldberg. Partial evaluation techniques for concurrent programs. In Proceedings of Partial Evaluation and Semantics-Based Program Manipulation (PEPM'97) [ACM97], pages 47{62.

[MMAY95] Hidehiko Masuhara, Satoshi Matsuoka, Kenichi Asai, and Akinori Yonezawa. Compiling away the meta-level in object-oriented concurrent re ective languages using partial evaluation. In Proceedings of OOPSLA'95 (SIGPLAN Notices Vol.30, No.10), pages 300{315, 1995. [MMWY92] Hidehiko Masuhara, Satoshi Matsuoka, Takuo Watanabe, and Akinori Yonezawa. Object-oriented concurrent re ective languages can be implemented eciently. In Proceedings of Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), pages 127{145, Vancouver, B.C., October 1992. ACM. [MMY96]

Hidehiko Masuhara, Satoshi Matsuoka, and Akinori Yonezawa. Implementing parallel language constructs using a re ective object-oriented language. In Re ection Symposium'96, San Francisco, CA, April 1996.

[OTY97]

Yoshihiro Oyama, Kenjiro Taura, and Akinori Yonezawa. An ecient compilation framework for languages based on a concurrent process calculus. In Proceedings of Euro-Par '97 Object-Oriented Programming, Lecture Notes in Computer Science, Passau, Germany, August 1997. Springer-Verlag.

[Smi84]

Brian Cantwell Smith. Re ection and semantics in Lisp. In Conference record of Symposium on Principles of Programming Languages, pages 23{35, 1984.

[Sun97]

Sun Microsystems. Java(TM) core re ection: API and speci cation, 1997.

[TY96]

Kenjiro Taura and Akinori Yonezawa. Schematic: A concurrent object-oriented extension to scheme. In Proceedings of Workshop on Object-Based Parallel and Distributed Computation, number 1107 in Lecture Notes in Computer Science. Springer-Verlag, 1996.

[WY88]

Takuo Watanabe and Akinori Yonezawa. Re ection in an object-oriented concurrent language. In Proceedings of Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), pages 306{315, San Diego, CA, September 1988. ACM. (revised version in [Yon90]).

[Yon90]

Akinori Yonezawa, editor. ABCL: An Object-Oriented Concurrent System. MIT Press, Cambridge, MA, 1990. 16

Suggest Documents