%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Exploiting multicore processors. % We show how to use Mozart's transparent distribution % layer to exploit multi-core processors. All the % examples have been run on a dual-core MacBook Pro % with Mozart 1.4.0. % In these examples, we use the remote module manager % to execute any Oz code on a second process. In a % dual-core machine, this second process will run on % the second core. The Oz code is wrapped in a % functor, so it can access resources (file system, % operating system, etc.) in the second process. % This code can easily be extended to handle failures, % e.g., if the second process is killed. This uses % the fault streams as explained in the Mozart % distribution tutorial. % This code will also work if the second process is % on another machine. See the documentation of the % Remote module for an explanation how this works. % To understand how Mozart's distribution layer works, % you can read Raphael Collet's Ph.D. thesis: % http://www.info.ucl.ac.be/~pvr/raphthesis.pdf % Author: Peter Van Roy % Date: Feb. 11, 2010 % Dummy computation for the examples declare fun {Fibo N} if N<2 then 1 else {Fibo N-1}+{Fibo N-2} end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % First step: create a remote module manager. % Create a second process accessed through remote module manager R. % {R apply(F ?M)} will execute functor F on the second process and % return the module M. The second process will execute on the % second core of a dual core machine. declare R={New Remote.manager init(fork:sh)} %%%% More than two cores. % If running on a machine with more than two cores, you can % run the above statement several times (with R1, R2, R3, etc.). % Each time a new process and a remote module manager will be % created. You can extend the examples given below to use all % the remote module managers at the same time to keep all cores busy. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Example executions % Example execution on the second core declare functor F export x:X define X={Fibo 25} end M={R apply(F $)} {Browse M.x} % Example execution that uses a resource on the second core % (here it is the OS module; it could be e.g., the file system) declare functor F import OS export t:T define T={OS.time} end M={R apply(F $)} {Browse M.t} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % An abstraction to create remote threads % Encapsulate the remote execution when no remote resources are used. % P is an arbitrary nullary procedure executed in a remote thread. % We're not interested in the remote module in this case. declare proc {RThread P} {R apply(functor define thread {P} end end _)} end % Execution that will keep both cores busy declare F1 F2 in {RThread proc {$} F1={Fibo 37} end} % Remote thread thread F2={Fibo 37} end % Local thread {Browse F1#F2} % Note that F1 is bound using *distributed lexical scoping*: % F1 is bound on the remote process and because of lexical scoping % it can be read on the the local process. To make this work, % Mozart's distribution layer uses a distributed binding protocol % (it is actually a complete distributed unification protocol). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % An abstraction to do remote function calls % Another way to encapsulate: take any one-argument function F % and convert it into a remote function execution. declare fun {RemoteF F} fun {$ X} M={R apply(functor export res:Res define Res={F X} end $)} in M.res end end % Create a 'remote' Fibo function declare RemoteFibo={RemoteF Fibo} % Another way to keep both cores busy {Browse thread {RemoteFibo 37} end + thread {Fibo 37} end} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%