UvA-DARE (Digital Academic Repository)
Towards reconfiguration and self-adaptivity in S-Net
Penczek, F.; Scholz, S.-B.; Grelck, C.
Publication date
2008
Document Version
Submitted manuscript
Published in
20th International Symposium on the Implementation and Application of Functional
Languages (IFL 2008)
Link to publication
Citation for published version (APA):
Penczek, F., Scholz, S-B., & Grelck, C. (2008). Towards reconfiguration and self-adaptivity in
S-Net. In S. B. Scholz (Ed.), 20th International Symposium on the Implementation and
Application of Functional Languages (IFL 2008) University of Hertfordshire, School of
Computer Science. http://staff.science.uva.nl/~grelck/publications/PencSchoGrelIFL08.pdf
General rights
It is not permitted to download or to forward/distribute the text or part of it without the consent of the author(s)
and/or copyright holder(s), other than for strictly personal, individual use, unless the work is under an open
content license (like Creative Commons).
Disclaimer/Complaints regulations
If you believe that digital publication of certain material infringes any of your rights or (privacy) interests, please
let the Library know, stating your reasons. In case of a legitimate complaint, the Library will make the material
inaccessible and/or remove it from the website. Please Ask the Library: https://uba.uva.nl/en/contact, or a letter
to: Library of the University of Amsterdam, Secretariat, Singel 425, 1012 WP Amsterdam, The Netherlands. You
will be contacted as soon as possible.
UvA-DARE is a service provided by the library of the University of Amsterdam (https://dare.uva.nl)
Download date:26 Nov 2021
Towards Reconfiguration and Self-Adaptivity in
S-Net
— Draft —
Frank Penczek1 , Sven-Bodo Scholz1 and Clemens Grelck1,2
1
University of Hertfordshire
Department of Computer Science
Hatfield, Herts, AL10 9AB, United Kingdom
{f.penczek,s.scholz,c.grelck}@herts.ac.uk
2
University of Amsterdam
Institute of Informatics
Kruislaan 403, 1098 SJ Amsterdam, Netherlands
[email protected]
Abstract. Stream processing is a well-suited model for parallel programming, as it allows the programmer to design parallel algorithms
intuitively by arranging computational tasks into a data-flow graph and
consecutively constructing a streaming network from it. However, a network that was designed with a specific workload in mind will not work
optimally if anticipated parameters, as for example data rates or computational costs per data item, change over time. To nonetheless achieve optimal performance, a restructuring of the network and re-implementation
of computational components is inevitable.
As the deployment of a revised network usually causes service disruption, we present a system that supports reconfiguration of streaming
networks at runtime. The reconfiguration of networks can either be triggered externally, i.e. initiated by the user, or from within the network
itself (self-adaptation) by, for example, monitoring certain runtime parameters.
Our system is based on S-Net, a coordination language for asynchronous
stream processing systems. S-Net supports the simultaneous use of computational components implemented in a range of programming languages and it offers network combinators to construct streaming networks from these components. We will introduce S-Net and extensions
of the language that allow for reconfiguration and self-adaptation of networks at runtime. The extensions are designed as network combinators
and integrate seamlessly into the existing language.
1
Introduction
Parallel programming, which has traditionally been used for high-performance
computing on large-scale parallel architectures, is going mainstream. The broad
availability of inexpensive multi-core CPUs drives the demand for general purpose programming languages that enable programmers to harness the full computational power of these novel architectures. For languages as C, C++, Java,
Fortran etc. several APIs are available that extend these languages by facilities
for parallel programming. The APIs allow programmers to use their language
of choice and to re-use legacy code but are rather low-level: MPI and PVM require explicit communication management via send- and receive calls. APIs as
PThreads, which implement communication via shared memory, require explicit
synchronisation and locking to ensure correct and deadlock-free execution. These
APIs also require manual assignment of work to available processes or threads.
OpenMP offers an higher-level approach through directives, but requires special
compiler support. All these approaches intertwine management code and computational code which makes programming an error-prone process and obfuscates
the resulting code.
On the other end of the spectrum, there are languages with (partially) implicit parallelism. This high-level approach, which is taken by for example languages as SAC or Data Parallel Haskell, takes away the burden of implementing
low-level and often quite complex tasks of work distribution, communication
and synchronisation. Being able to entirely focus on the design of an algorithm,
turns programming into a much more efficient process and leads to better readable and maintainable code. The only drawback is that programmers need to
learn how to use these languages; legacy code cannot be reused and needs to be
reimplemented.
To combine the advantages of the above approaches but not their disadvantages, we propose a coordination language that strictly separates code for
computation from code for coordination of computation. The model we have
developed distinguishes between computational components that interact with
each other and the design of a communication network over which the interaction
is carries out. The computational aspect is kept as a separate concern – in fact,
we allow arbitrary languages for the implementation of the computational components. The focus of the proposed language lies on the coordination aspect. It
provides combinators to construct a streaming network which contains computational components as nodes. This approach especially supports reuse of legacy
code without cluttering it up with code for the traditional APIs, as all communication is defined outside the computational components. It also allows for a
very lean language design, as computational aspects are left to a programming
language of the user’s choice.
As indicated above, our system is in fact a coordination language for stream
processing networks. Stream processing is a well-suited model for parallel programming, as it allows the programmer to design parallel algorithms intuitively
by arranging computational tasks into a data-flow graph and consecutively constructing a streaming network from it. The amount of exposed concurrency is
influenced by the design decisions made for decomposition of the problem into
separate computational components, as each component may potentially be assigned to different computing resources. The topology of the network, which is
constructed with the provided combinators, defines the communication scheme
of the program. This allows development of parallel programs on a high level
of abstraction and in an intuitive way: The program design is representable as
an box-and-arrow diagram which hides the low-level and often complex requirements of communication and synchronisation issues.
Ideally, any streaming network is designed with a target system in mind. The
granularity of the decomposition is chosen to match the computing resources of
the target system to generate an optimal workload. This, of course, requires
knowledge about the target. If this knowledge is not available, a programmer
can only design a very generic model. However, a network that was designed
with a specific workload in mind will not work optimally if anticipated parameters, as for example data rates or computational costs per data item, change over
time. To nonetheless achieve optimal performance, a restructuring of the network
and re-implementation of computational components is inevitable. Once this reengineering has taken place, the generic model is superseded by a more specialised implementation which then is deployed. As the deployment of a revised
network usually causes service disruption, our system supports reconfiguration
of streaming networks at runtime. The reconfiguration of networks can either be
triggered externally, i.e. initiated by the user, or from within the network itself
(self-adaptation) by, for example, monitoring certain runtime parameters.
Our approach to provide these facilities is twofold. Firstly, we promote networks to first class citizens of the language. That is, data items that are sent
across the network may be networks itself. Secondly, we introduce a replacement
combinator. This combinator marks (sub-)networks as replaceable by new versions of networks which it receives over the stream. The combinator ensures that
replacement of networks is only carried out if a received network is compatible,
i.e. we guarantee that the replacement process will not break the specified semantics of the overall system. This approach enables a user to react to changes of the
environment, e.g. increase or decrease of resources, bandwidth changes, etc., and
reconfigure the system accordingly by sending revised versions of sub-networks
over the existing network infrastructure.
As networks are first class citizens, revised versions of networks may also be
created and emitted from withing networks itself. Combining this with a feedback
combinator, our design is powerful enough to implement self-adaptive networks
that dynamically reconfigure themselves based on runtime measurements of, for
example, throughput or execution times.
2
Introducing S-Net
to be included in the final version of this paper
3
Introducing S-NetΩ
to be included in the final version of this paper
4
S-Net Language Components and Semantics
Throughout the following chapters we shall use the following notation: A single
italic letter, as for example p, denotes a single record. A single letter with an
arrow on top denotes a stream of records, e.g. ~p. The concatenation of two
streams is denoted by p~ ++ ~q (appending ~q to ~p), concatenation of an element
and a stream by p ⊳ p~ (prefixing ~q by p) and ~q ⊲ p (appending p to ~q). An n-fold
concatenation, denoted by ++ni=1 (~
pi ), expands to ~p1 ++ p~2 ++ ... ++ p~n .
4.1
Boxes and Primitive Boxes
Boxes are the only components in S-Net that are able to process and modify
data of record fields. On input of a single record, a box may produce an arbitrary
number (including zero) of result records. A distinctive feature of S-Net is
the fact that function f of rule Box may be implemented in an arbitrary
programming language.
f (p) = ~q
Box : (p, boxf ) → (boxf, ~q)
A synchro cell, the only stateful component in S-Net, is a facility to store
and merge records that match a user-defined pattern. For this, synchro cells are
parametrised over two patterns. Any inbound record is matched against these
patterns. If a record matches a previously unmatched pattern, it is stored by the
synchro cell, the pattern is marked as matched and no output is produced (rule
Syncs ). An index at the lower-right corner of the synchro cell indicates this. If
a pattern was matched before, the record is simply output again, as described by
the Syncn rule. A record that matches the last remaining unmatched pattern
triggers a merge of stored and inbound record. After merging and outputting
the resulting record, the synchro cell will act as an identity component ( Syncm
rule). If a record immediately matches both patterns, the record passes unmodified and the synchro cell again will act as an identity component, as shown by
the Synci rule.
Syncs
ismatch(σa , pa ) ∧ ¬ismatch(σb , pa )
:
(pa , Jσa , σb K) → (Jσa , σb Kpa , ǫ)
Syncn
ismatch(σa , p) ∧ ¬ismatch(σb , p)
: (p, Jσa , σb Kpa ) → (Jσa , σb Kpa , p)
Synci
:
ismatch(σa , p) ∧ ismatch(σb , p)
(p, Jσa , σb K) → (id, p)
ismatch(σb , p)
Syncm : (p, Jσa , σb Kpa ) → (id, merge(pa , p))
The filter (see rule Filter ) is a versatile instrument to modify the structure
of records. Filters can split records, rename, copy or discard fields and tags and
even insert new tags and modify their value. The behaviour of each filter is
controlled by a pattern σ and by a user-defined list α of aforementioned actions.
The pattern defines which constituents of inbound records are accessible by the
filter actions. Only fields and tags that are present in the pattern may be used
in the action list. On arrival of a record, the actions are applied to the inbound
record and all resulting records are output. The S-NetΩ filter is also equipped
with pattern matching facilities for network functions. With this, it is possible
to assign identifiers to sub-expressions of a function on the left-hand side of the
filter and use these identifiers on the right-hand side to construct new network
functions. The filter also features runtime variables that contain values of the
current system clock tick and the average throughput seen by the filter. These
values may be assigned to tags for later use.
~q = apply(α, p)
Filter : (p, [σ -> α]) → ([σ -> α], ~q)
4.2
S-Net Combinators
The four S-Net combinators, which are used to construct networks from boxes,
primitive boxes and networks, are very briefly introduced here.
The serial combinator connects the output of its left operand to the input of
its right operand. The output of the right operand thereby forms the output of
the newly constructed network.
(~
p, M ) → (M ′ , p~′ ) (p~′ , N ) → (N ′ , ~q)
(~
p, M..N ) → (M ′ ..N ′ , ~q)
Ser :
The choice combinator creates a new network by connecting its two operands
in parallel. Records are routed to either of the operands depending on which
operand is a more specific match. The specificity of the match is determined by
analysing the type of the inbound record and the input type of the operands.
If both operands match equally well, one is chosen non-deterministically. The
choice combinator comes in two variants, deterministic and non-deterministic.
The deterministic variant preserves the order of inbound and resulting outbound
records. If record order is not a concern, the non-deterministic variant may be
used. The Dchoice rule formalises the behaviour of the deterministic choice
combinator.
∀ni=1
Dchoice :
p, τM , τN )
(p~li , p~ri )i∈{1,..,n} = lrpsplit(~
′
′
)
: (p~li , Mi ) → (q~li , Mi+1 ) ∧ (p~ri , Ni ) → (q~ri , Ni+1
(~
p, M1 ||N1 ) → (Mn+1 ||Nn+1 , ++ni=1 (q~li ++ q~ri ))
The inbound stream ~
p is divided into pairs of sub-streams (p~li , p~ri ) such that
each (potentially empty stream) p~li (resp. p~ri )) contains records matching the
input type of the left (resp. right) operand network. These pairs are processed
by the operand networks, whose internal state may change due to synchro cells,
and produce streams q~li and q~ri as result. The output streams are concatenated
to a result stream and represent the result for one input pair. The overall outbound stream is constructed by concatenating result streams of all input pairs.
The behaviour of the non-deterministic choice combinator is expressed by the
NDchoice rule.
p~l , p~r = lrsplit(~
p, τM , τN )
(~
pl , M ) → (M ′ , q~l ) (p~r , N ) → (N ′ , q~r )
NDchoice :
(~
p, M |N ) → (M ′ |N ′ , ndzip(~
ql , q~r ))
Any inbound stream of records p~ is divided into two separate sub-streams p~l and
p~r , such that each record in p~l (resp. p~r ) matches the inbound type of the left
(resp. right) operand of the choice combinator. The operand networks, whose
internal state may change due to synchro cells, produce q~l and q~r as results.
These result streams are non-deterministically merged, i.e. both streams may be
arbitrarily interleaved.
The star combinator requires an operand and a pattern for operation. Any
inbound record that matches the pattern is immediately output. If the record
does not match the pattern, it is sent to the operand. At the output of the
operand, the same process repeats. If the result matches the patten, it is output, otherwise a new instance of the operand is spawned and the record is sent
to it. The star combinator is available as deterministic and non-deterministic
combinator. The deterministic variant guarantees that any result of an earlier
input will exit the network before any other result of a later input, i.e. outbound
streams of multiple inbound records are not interleaved and in the same order
of their corresponding inbound records.
Dstar
(~
p, (M..M ∗{σ})||[σ -> τσ ]) → (M ′ , ~q)
(~
p, M ∗∗{σ}) → (M ′ , ~q)
:
(~
p, (M..M ∗{σ})|[σ -> τσ ]) → (M ′ , ~q)
NDstar :
(~
p, M ∗{σ}) → (M ′ , ~q)
The split combinator routes inbound records to different instances of its
operand depending on the value of a specified tag. The name κ of the tag that
determines the instance of the operand is given as parameter to the split combinator. Each instance of the operand stays associated with the combinator and is
reused whenever a record is processed that holds the appropriate instance value.
[todo: The Dsplit is too restrictive and has to be defined on streams to refelct
the fact that records of different branches can be processed concurrently. Add
subscript or different font for N to annotate associated set of instances.]
value(κ, p) = j
Dsplit
:
(p, Nj ) → (Nj′ , ~q)
(p, N !!κ) → (N !!κ, ~q)
∀ni=1 pi ∈ ~p : value(κ, pi ) = vi
NDsplit :
4.3
(pi , Nvi ) → (Nv′ i , ~qi )
(~
p, N !κ) → (N !κ, ndzip(~q1 , ..., ~qn ))
Id, Empty and Map Rule
The following rules are merely ’helper-rules’. The Id rule allows records to
be passed on without any alteration. An empty stream has no effect on any
component, as shown by the Empty rule. The Map rule allows to derive a
semantics for streams if component rules are defined on single records.
Id
: (p, id) → (id, p)
Empty : (ǫ, M ) → (M, ǫ)
∀ni=1 : (pi , Mi ) → (Mi+1 , q~i ) ~s = ++ni=1 (~
qi )
Map
4.4
:
(~
p, M1 ) → (Mn+1 , ~s)
Algorithms
eval
eval :: pattern → record → Bool
eval σ p =
...
ismatch
ismatch :: pattern → record → Bool
ismatch σ p =
p 4 σ ∧ eval σ r
score
score :: pattern → record → N0 ∪ {−1}
score σ p =
...
bestmatch
bestmatch :: record → pattern → pattern → set of pattern
bestmatch r σl σr =
{σi | i, j ∈ {l, r} ∧ score σi r ≥ score σj r}
prefix
prefix :: stream → pattern → pattern → (stream, stream)
prefix p~ σl σr =
case ~
p of
ǫ → (ǫ,ǫ)
p : ps
~ | ndsel (bestmatch p σl σr ) == σl → (p:~q, ~r)
p : ps
~ | otherwise → (ǫ, p~)
where
(~
q , ~r) = prefix ps
~ σl σr
splitOnePair
splitOnePair :: stream → pattern → pattern → (stream, stream, stream)
splitOnePair p~ σl σr =
case ~
p of
ǫ → (ǫ, ǫ, ǫ)
p : ps
~ → (~
ql , q~r , ~r)
where
(~
ql , ~t) = prefix p~ σl
(q~r , ~r) = prefix ~t σr
lrpsplit
lrpsplit :: stream → pattern → pattern → (stream of streams, stream of
streams)
lrpsplit ~
p σl σr =
case ~
p of
ǫ → (~ǫ,~ǫ)
~
p : ps
~ → (l~p : l~pl , r~p : r~~pl )
where
(l~p , r~p , ~
q ) = splitOnePair p~ σl σr
~
~
~
(lpl , r~pl ) = lrpsplit ~q σl σr
lrsplit
lrsplit :: stream → pattern → pattern → (stream, stream)
lrsplit ~
p σl σr =
let (p~
~l , p~
~r ) = lrpsplit ~
p σl σr in
(flatten p~
~l , flatten p~
~r )
merge
merge :: record → record → record
record p~ ~q =
p~ ∪ ((~
q \ BT (~
q )) \ (~
p ∩ q~))
apply
apply :: record → action → stream
apply p α =
...
5
Components and Semantics of the Extended Language
We extend the S-Net core language by two combinators which will be powerful enough to allow for reconfiguration as well as self-adaptation scenarios. As
S-NetΩ is an extension to S-Net, it includes all combinators of the original
language. In this paper, all components that are shared between S-Net and
S-NetΩ will be referred to as S-Net components, whereas components that are
not part of core language will be referred to as S-NetΩ components.
5.1
Special Records and Tags
to be included in the final version of the paper
5.2
Network Combinators
The ρ-combinator replaces its current network function if an inbound record
contains a compatible replacement function. A network function is considered
compatible if it is a subtype of the current function. Any record that does not
carry a suitable replacement function is processed by the current network function.
∃Fp ∋ f 4 M (p, f ) → (X, ~q)∨
∀Fp ∋ f 64 M (p, M ) → (X, ~q)
Rho :
(p, ρ[M ]) → (ρ(X), ~q)
The µ-combinator constitutes a feedback combinator. Similar to the star
combinator, the behaviour of this combinator is influenced by a user-defined
pattern. The pattern is matched against inbound records at the output of the
network the combinator is applied to. Records that do not match the specified
pattern are output. In this case, the combinator has no observable effect. All
records that match the pattern are re-inserted to the operand network. As the
Mu shows, the feedback path of the combinator has priority over the inbound
stream. Records are only read from the inbound stream when all records of the
feedback path have been processed. A non-deterministic variant of this combinator, which relaxes this strict behaviour and allows for interleaved feedback and
inbound record streams, is currently under investigation and remains as future
work.
(p, M ) → (M ′ , q~′ ) ~l, ~r = lrpsplit(q~′ , σ, {})
(~l, M ′ µ{σ}) → (M ′′ µ{σ}, ~q)
Mu :
6
(p, M µ{σ}) → (M ′′ µ{σ}, ~q ++ q~′ )
Example: Reconfiguration and Adaptation Based on
Throughput Monitoring
to be included in the final version of the paper
7
Related Work
to be included in the final version of the paper
8
Conclusion and Future Work
We have presented a coordination language for stream processing that natively
supports reconfiguration and restructuring of the network topology and its components. This was achieved by promoting networks to first class citizens of the
language. Due to this design, networks may not only be sent across the streaming network, they may also be emitted by components from within networks
itself. This further extended our system by adaptation abilities, which render
it possible to implement self-modifying networks. The self-adaptation process
can be triggered by various sources, as for example an increase or decrease of
available computing resources and fluctuating execution times of computational
components for inbound data.
Rather than providing reconfiguration and adaptivity facilities implicitly and
hiding it from the user, e.g. by implementing these as opaque mechanisms of a
runtime system, we designed these as a native part of the language. By extending
the core S-Net language by only two combinators, we empower users to explicitly implement reconfiguration and self-adaptation scenarios. A distinct feature
of our approach is the possibility to replace networks by new versions without
having to specify alternative implementations at deploy or even design time of
the original network.
Although our approach enters the domain of self-modifying code, the system
guarantees static properties: For any data item that enters the network, it is
guaranteed that a path exists for it through the entire network. Replacement of
networks is only carried out if a newly received network is of compatible type
and thus does not break the specified semantics. The main enabling factor for
this is the strict separation of computational languages and the coordination
language as this allows for a very concise type system.
What we have presented in this paper is work in progress. A non-deterministic
variant of the replacement and feedback operators are under development. The
record-discarding behaviour of synchro-cells at replacement events is not fully
satisfactory and may need refinement. Extended semantics for this and a formal
definition of the type system for S-NetΩ remain as future work.