• Keine Ergebnisse gefunden

Data Transfer and Execution Model

Im Dokument 3D visualization of multivariate data (Seite 125-128)

Array[0..99]

OPX Data1

Data2

2 2

Array[0..99]

[0..49]

[50..99]

Data2 Data1

[50..99]

[0..49]

SplitOPX

OPX1

OPX2 MergeOPX

Array[0..99]

Array[0..99]

Figure 7.2: Domain decomposition of operator X. The operator receives an array of variable length (100 elements in this example). These elements are processed depending on two additional data elements, Data1 and Data2. Domain decomposition is achieved by creating multiple instances of the operator (two in this example). A split operator distributes the work equally to all operator instances (green lines). It also sends the incoming additional data elements to all operator instances. Finally a merge operator concatenates the partial results (red lines).

nodes or to one node respectively.

7.2.4 Domain Decomposition

Figure 7.2 illustrates how domain decomposition was realized with NexusDS for an example where an operator is decomposed into two independently working operators.

The number of array elements may change for each data packet. Moreover, the number of operators can vary, as specified by the module parameters during module initialization.

7.3 Data Transfer and Execution Model

7.3.1 Efficient Communication

NexusDS is implemented in Java. For the development of efficient visualization operators the C++ language was chosen. The Java Native Interface (JNI) is used to call C/C++

code from Java. To implement efficient communication between the C++ visualization operators over the Java NexusDS frameworkjava.nio.ByteBufferobjects were used.

These objects are wrappers for C++ memory blocks, so that C++ objects can be handed

JNI

Java - NexusDS Operator C/C++ - NexusDS Op.

JNI

Java - NexusDS Operator C/C++ - NexusDS Op.

Direct BufferByte NexusDS

C-Obj. C-Obj.

Figure 7.3: Operators send direct byte buffers over the JNI interface.

over to Java without copying the data itself. The Java NexusDS system can transport references to data between operators if the operators are on the same node, see Figure 7.3.

A design decision was made not to use linked but flat data structures. If the operators run on different nodes, flat data structures also prove beneficial. Since the data is already in a linear memory block, it can be sent over the network and stored in a local memory block on the receiver side. For this task NexusDS was extended with platform sinks and sources which can handle direct byte buffer objects, using thejava.nio.ByteBuffer

class.

7.3.2 Memory Management

The operators allocate memory for their outputs on the native (C++) side. They just hand over constant references to other operators, i.e., operators are not allowed to modify input data, but they can output more than one reference to the same memory to different operators. The communication channels are unidirectional and thus there is no mechanism to explicitly tell an operator that a given input is not needed any more.

Therefore, a different mechanism was implemented: the input becomes invalid after the next data packet has been received on the same input port. Analogously, the memory of an operator output has to be kept until another output element is sent via the same port.

This implies that the receiving operator should block if data is coming through an input port when the previous element that came through the same input port is still needed for computations. In contrast to the solution presented by Ahrens et al. [ALS+00], local copies are entirely avoided using this paradigm. As NexusDS uses internal queues for the input ports, the number of outputs an operator has to keep increases with the size of these queues.

NexusDS was configured to use first-in, first-out (FIFO) queues in the system. The size of the input queues needs to be small to keep latencies low. In case of uneven concentration of incoming events the queues should be increased to allow high utilization of nodes. (This is not an issue in the example scenario of Section 7.4.)

7.3. Data Transfer and Execution Model 113

Java Operator C++ Operator InputQueues

Figure 7.4: This operator has three input and two output ports. Threads running inside the operator—denoted as red, blue, and green arrows—are associated with the input queues.

7.3.3 Thread Model

NexusDS is responsible for the data transfer between operators and also provides an execution model. An operator can have several input ports, see Figure 7.4. Each port has an input queue for incoming data. NexusDS was configured to associate one thread to each input queue, this thread is waiting until the input queue is not empty. Then the respective input method of the Java operator is called, which calls the native input method of the C++ operator. The operator processes the data and eventually outputs results to some output ports. Since each input queue delivers input data to the operator concurrently, synchronization has to be performed by the C++ operator.

7.3.4 Communication over the JNI

For the communication between the Java and the C++ side the Call-Invoke pattern [WK00] was used. To output data from the C++ operators each operator has one registered listener that sends the output of all output ports back to the Java part of the operators.

Resolving a C++ Object over the JNI

It is not possible to call a C++ object method over the JNI, but only C functions. In case of multiple operator instantiations, it is therefore necessary to call the right instantiation.

This problem is solved by storing a C-pointer inside the Java classes that is passed to the C function as an additional argument, which is then used to resolve the right C++ object.

Figure 7.5: Air flow visualized on a mobile client.

Multi-threaded Execution

Since a separate thread is associated with each input port, the operators receive data in different execution threads. To resolve the Java object and the method for sending back the operator output, a JNI interface pointer and the jobject pointer are needed. These are valid only for the thread associated with it [Lia99]. By saving these pointers in thread local storage it is possible to resolve the right pointers and allow input queue threads to run concurrently on the C++ side.

Im Dokument 3D visualization of multivariate data (Seite 125-128)