• No se han encontrado resultados

Capítulo 3. Material y métodos

3.4. Análisis estadístico

Using Terra and making use of the advantages ofMSPin a fully custom language is not entirely trivial. A special separate compiler architecture is needed, which constructs Terra statements and functions using multi-stage operators. In this instance, this additional compiler architecture includes a parser, a type checker, and a code generator. The Terra libraries provide good support for writing these in Lua. The addition of dynamic typing and garbage collection in Lua allows for simpler tree structures and simpler traversals.

5.3.1

Lattice-oriented ABM

At this point, the language caters for lattice-oriented agent-based models only, where agents are homogeneous. That is to say that agents are identical, and therefore execute the same code. They are, however, not restricted to exactly the same code flow. The final objective of the compiler is to generate a valid and semantically correct Terra function which conforms to the following signature:

terra(rolattice: &int ,wolattice: &int ,scores: &double,frame: long).

This permits the host program to maintain the lattice itself and visualise the results in real time. It is not necessary to force the host program to maintain lattices, however. Terra is capable of using C functions to allocate memory very simply. In this instance, it was chosen to allocate and maintain these in the host program in order to simplify rendering.

Figure5.1indicates the flow that theDSLframework would follow during startup. When the Lua parser encounters amoltoken, the special MOL parser will process this syntax, then hand a parse tree to a type checker, and finally the type-checked parse tree is passed to a code generator. During the code generation phase, the compiler will emit Terra statements and expressions, which are combined together to form a Terra function with the signature given above, which is then JIT-compiled.

MOL is equipped with an extensible custom library, which makes available common functionality in lattice- based simulations. Features such as random numbers and movement are provided in this library. This also presents an opportunity to specialise code for the benefit of the simulation, as library functionality is made available in the form of Terra statements and expression generators. Thanks to meta-methods and Terra structures, data types such as 3D integer vectors and 3D floating point vectors can support various binary and unary operators. These structures can be used within theDSLdescribed here quite simply, due to the shared lexical environment of Lua and Terra. Introducing other structures would be almost trivial.

5.3. THE MOL COMPILER 113

Parser Type Checker DSL-specific Compiler Architecture

Compile to LLVM IR Code Generator DSL Code Terra Terra Code Generator

Resolve DSL Library Usage Resolve Variables Build expressions Build Terra Statements

Simulation Constructor

Generate n calls to agent function Splice into new Terra function

Return Function

Compile Reset Function Compile Frame Event Function

Pass function pointer through Lua to Host C++ program

Host calls function

114 5. DOMAIN SPECIFIC LANGUAGE FOR ABM

Following the code generation phase, Terra statements are then passed to Terra, which compiles the syntax tree to the internal representation (IR) of LLVM [159]. This is then compiled using one of the standard LLVM backends for the X86 instruction set (in this case). At this point however, the process is not complete. What has been compiled to machine code is a function with the following signature:

terra(rolattice: &int ,wolattice: &int ,me: int , score: &double, x: int , y: int , z: int )

When called with the correct arguments, this function will execute the set of agent instructions for precisely one agent. What is needed is to construct another Terra function which callsthisfunctionntimes with the correct arguments each time in order to cover the entire lattice. This is indicated as the “simulation constructor” in Figure5.1. Fornlattice sites, a call to this function is generated and spliced into a list of Terra statements. Listing5.4contains a short code excerpt showing how a Terra function is constructed for computing the same agent behavioural code on all lattice sites. Essentially, it is possible to change this framework into a heterogeneous agent-based simulation platform by either differentiating between different types of agents in the DSL code, or linking different programs within this code.

1 local myprogram = molcompiler.compile(prototype.typedtree)

2 local temp =terra([ rolattice ], [wolattice], [scores])

3 for x = 0, [ sim grid x ] do

4 for y = 0, [ sim grid y ] do

5 for z = 0, [ sim grid z ] do

6 [myprogram](rolattice, wolattice,

7 rolattice[ z∗[sim grid x ]∗[ sim grid y ] + y∗[sim grid x ] + x ],

8 [&double](scores), x,y,z 9 ) 10 end 11 end 12 end 13 end 14 temp:compile() 15 parameters.setStepallFunc(getFunctionPointer(temp))

LISTING5.4: Lua-Terra code excerpt showing how a Terra function is constructed to execute the same agent program on all lattice sites.

The escape operator[ ]is used to splice Lua values into Terra code. These may include Terra statements or expressions and these are evaluated at compile time (therefore Lua values are essentially constant). More details on this is available [46]. Once the function is constructed, it is compiled and the function pointer is passed back to the host program using a Lua function. Along with this function which will compute an entire time step given the lattice arrays from the host, two other functions are also compiled and passed to the host program. These functions facilitate a lattice reset and an extra function to execute on every timestep computation. Additional computation during frames can be costly, and therefore, the frame event can be disabled easily. This is useful for gathering data into separate files, and the reset function is simply used to initialise the lattice.

As an example of the process of compiling staged code, the example shown in Listing5.2is specialised into the code shown in Listing5.5. This code is then compiled into the LLVM IR code shown in Listing5.6. Finally, this IR code is then compiled using a backend of LLVM, which in this case would be the X86 backend.

The parser, type checker and the code generator for MOL are implemented in a recursive manner, which are highly influenced by the example code provided with Terra [46]. Syntax trees (type-checked and otherwise) are

5.3. THE MOL COMPILER 115

1 terratest t 14 0 = terra($x : int32 ) : {int32}

2 return $x∗$x∗$x

3 end

LISTING5.5: The Terra code generated by the staging example shown in Listing5.2. The result is a compiled function which computes the cube of an input integer.

1 define i32 @__terratest_t_14_0(i32) {

2 entry:

3 %1 = mul i32 %0, %0

4 %2 = mul i32 %1, %0

5 ret i32 %2

6 }

LISTING5.6: The LLVM IR code generated by the staging example shown in Listing5.2. This code is the final stage before a LLVM backend compiles the code to machine instructions.

constructed using Lua tables, making full advantage of the fact that Terra statements, expressions and functions are all first-class Lua values.

5.3.2

Spatial ABM

Though the emphasis in this chapter is given to lattice-oriented ABM, a spatial version of the MOL language was also experimented with. It is easier to see the efficacy of MOL on lattice-based models, but it is certainly possible to cater for spatial ABM also, and this will be briefly detailed here.

Where the compiler would generate an agent Terra function which takesint-lattices, the spatial version generates an agent function with the following signature:

terra( positions : &float , velocities : &float ,scores: &float ,frame: long)

In fact, the spatial version of MOL demonstrates an interesting effect which is a direct consequence of the use of LLVM. A very short performance comparison is given here to illustrate this.

Although one might normally expect that code which has been automatically generated would be slower than hand written code due to the abstraction penalty, in this case it is found that the generated code is in fact almost an order of magnitude faster. The large difference in performance shown in Figure5.2is made less surprising by trivial inspection of the generated LLVM IR code, which yielded use of vectorised instructions, due to LLVM’s optimisers (in this case, for the AVX and SSE enabled Intel processor). All vectors in the spatial MOL language are stored as Terravectortypes, which are consequently mapped to AVX and SSE structures. This allows LLVM to make straightforward code optimisations. The MOL library stores various functionality for linear computations using vectors by providing abstracted Terra operators specified in meta-methods. Terra automatically applies these by inspecting metatables.

Listing5.12gives a very simple example of the spatial version of the MOL language. A similar structure can be seen to the Lattice-based MOL language. More examples of both are found in the next section.

The code shown in Listing5.8was compared in terms of raw performance against the C++ code in Listing5.7. The results of this were given in Figure5.2.

Another advantage ofMSPin this instance is obtained. Automatic parallelism is an important concept, especially since clock speeds are no longer increasing, but the number of processors and cores are [221,161]. Unfortunately

116 5. DOMAIN SPECIFIC LANGUAGE FOR ABM

FIGURE5.2: Performance comparison between hand written GNU C++ code (Listing5.7) and Terra (LLVM) compiled code (Listing5.8).

1 float3 goal = make_float3( 0.1f,0.2f,0.3f );

2 for (int i=0; i < params->agent_count; ++i) {

3 float3 *mypos = (float3*)(h_positions + i*3);

4 float3 *myvel = (float3*)(h_velocities + i*3);

5

6 *myvel = *myvel + (goal - *mypos) * 0.001;

7 *mypos = *mypos + *myvel * 0.1;

8 }

LISTING5.7: C++ code used for comparison against the MOL code in Listing5.8. The code uses sometypedefs from CUDA.

1 mol

2 defvar goal ={0.1,0.2,0.3}

3 movetowardsgoalby0.001

4 pos = pos + 0.1∗vel

5 end

6

LISTING 5.8: Custom DSL code used to compare against the GNU C++ code in Listing5.7.

Documento similar