Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
276 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
.. _pipeline: | ||
|
||
======== | ||
Pipeline | ||
======== | ||
|
||
Pipelines allow to arbitrarly interconnect generators and resources by chaining them. | ||
Messages belonging to a pipeline automatically flow from a resource to another without the needs of explicitly defining the `hops`. | ||
Please note that a generator (and their messages) belong only to a single pipeline. | ||
If multiple pipelines are needed simultaneously, they need to be merged fisrt. See :ref:`pipeline_branching` for more details. | ||
|
||
Single pipeline | ||
=============== | ||
|
||
Let's start with a simulation where messages are generated by ``Generator #0`` and are served by ``Resource #0`` and ``Resource #1``. | ||
|
||
.. code-block:: none | ||
|Generator #0| -> |Resource #0| -> |Resource #1| | ||
The SimPype code would hence be: | ||
|
||
.. code-block:: python | ||
import simpype | ||
import random | ||
sim = simpype.Simulation(id = 'single') | ||
gen0 = sim.add_generator(id = 'gen0') | ||
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)} | ||
res0 = sim.add_resource(id = 'res0') | ||
res0.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
res1 = sim.add_resource(id = 'res1') | ||
res1.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
p0 = sim.add_pipeline(gen0, res0, res1) | ||
sim.run(until = 5) | ||
As it can be noticed in ``sim.log`` file, messages generated by ``Generator #0`` automatically flow through ``Resource #0`` and ``Resource #1`` without the needs of explicitly defining the next hop: | ||
|
||
.. code-block:: none | ||
timestamp,message,seq_num,resource,event | ||
0.000000000,gen0,0,res0,pipe.default.in | ||
0.000000000,gen0,0,res0,pipe.default.out | ||
0.044474460,gen0,0,res0,resource.serve | ||
0.044474460,gen0,0,res1,pipe.default.in | ||
0.044474460,gen0,0,res1,pipe.default.out | ||
0.867233916,gen0,1,res0,pipe.default.in | ||
0.867233916,gen0,1,res0,pipe.default.out | ||
1.099185483,gen0,0,res1,resource.serve | ||
1.876512438,gen0,1,res0,resource.serve | ||
1.876512438,gen0,1,res1,pipe.default.in | ||
1.876512438,gen0,1,res1,pipe.default.out | ||
2.873364054,gen0,1,res1,resource.serve | ||
Overlapping pipelines | ||
===================== | ||
|
||
Noe let's continue with a simulation scenarios like the following: | ||
|
||
.. code-block:: none | ||
|Generator #0| -\ /-> |Resource #1| | ||
)-> |Resource #0| -( | ||
|Generator #1| -/ \-> |Resource #2| | ||
In this scenario we want to reproduce the following interconnection: | ||
|
||
.. code-block:: none | ||
|Generator #0| -> |Resource #0| -> |Resource #1| | ||
|Generator #1| -> |Resource #0| -> |Resource #2| | ||
As it can be noticed, there are two dinstinct `paths/pipelines` that overlap at ``Resource #0``. | ||
However, any messages generated by ``Generator #0`` should end to ``Resource #1``. | ||
Similarly, any messages generated by ``Generator #1`` should end to ``Resource #2``. | ||
|
||
In this scenario, ``Resource #0`` is hence shared between the two `pipelines`. | ||
The SimPype code would hence be: | ||
|
||
.. code-block:: python | ||
import simpype | ||
import random | ||
sim = simpype.Simulation(id = 'overlap') | ||
gen0 = sim.add_generator(id = 'gen0') | ||
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)} | ||
gen1 = sim.add_generator(id = 'gen1') | ||
gen1.random['arrival'] = {0: lambda: random.expovariate(1.0)} | ||
res0 = sim.add_resource(id = 'res0') | ||
res0.random['service'] = {0: lambda: random.expovariate(4.0)} | ||
res1 = sim.add_resource(id = 'res1') | ||
res1.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
res2 = sim.add_resource(id = 'res2') | ||
res2.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
p0 = sim.add_pipeline(gen0, res0, res1) | ||
p1 = sim.add_pipeline(gen1, res0, res2) | ||
sim.run(until = 2.5) | ||
As it can be noticed in ``sim.log`` file, messages generated by ``Generator #0`` automatically flow through ``Resource #0`` and ``Resource #1`` and messages generated by ``Generator #1`` automatically flow through ``Resource #0`` and ``Resource #2``. Moreover, ``Resource #0`` is shared between the two `pipelines`: | ||
|
||
.. code-block:: none | ||
timestamp,message,seq_num,resource,event | ||
0.000000000,gen0,0,res0,pipe.default.in | ||
0.000000000,gen1,0,res0,pipe.default.in | ||
0.000000000,gen0,0,res0,pipe.default.out | ||
0.372608250,gen0,0,res0,resource.serve | ||
0.372608250,gen0,0,res1,pipe.default.in | ||
0.372608250,gen0,0,res1,pipe.default.out | ||
0.372608250,gen1,0,res0,pipe.default.out | ||
0.515112655,gen0,1,res0,pipe.default.in | ||
0.636849329,gen1,0,res0,resource.serve | ||
0.636849329,gen1,0,res2,pipe.default.in | ||
0.636849329,gen1,0,res2,pipe.default.out | ||
0.636849329,gen0,1,res0,pipe.default.out | ||
0.653319564,gen0,1,res0,resource.serve | ||
0.653319564,gen0,1,res1,pipe.default.in | ||
0.684766776,gen1,1,res0,pipe.default.in | ||
0.684766776,gen1,1,res0,pipe.default.out | ||
0.851617505,gen0,0,res1,resource.serve | ||
0.851617505,gen0,1,res1,pipe.default.out | ||
0.921614468,gen1,2,res0,pipe.default.in | ||
0.949578262,gen1,1,res0,resource.serve | ||
0.949578262,gen1,1,res2,pipe.default.in | ||
0.949578262,gen1,2,res0,pipe.default.out | ||
1.052881475,gen1,2,res0,resource.serve | ||
1.052881475,gen1,2,res2,pipe.default.in | ||
1.079748898,gen0,1,res1,resource.serve | ||
1.245866822,gen1,3,res0,pipe.default.in | ||
1.245866822,gen1,3,res0,pipe.default.out | ||
1.352498249,gen1,0,res2,resource.serve | ||
1.352498249,gen1,1,res2,pipe.default.out | ||
1.369990105,gen1,4,res0,pipe.default.in | ||
1.384336838,gen1,1,res2,resource.serve | ||
1.384336838,gen1,2,res2,pipe.default.out | ||
1.385217621,gen1,5,res0,pipe.default.in | ||
1.418331444,gen1,2,res2,resource.serve | ||
1.582122574,gen1,3,res0,resource.serve | ||
1.582122574,gen1,3,res2,pipe.default.in | ||
1.582122574,gen1,3,res2,pipe.default.out | ||
1.582122574,gen1,4,res0,pipe.default.out | ||
2.028251841,gen1,4,res0,resource.serve | ||
2.028251841,gen1,4,res2,pipe.default.in | ||
2.028251841,gen1,5,res0,pipe.default.out | ||
2.148959938,gen1,6,res0,pipe.default.in | ||
.. _pipeline_branching: | ||
|
||
Branching pipeline | ||
================== | ||
|
||
Now let's continue with a pipeline having a branching point with one generator and three resources: | ||
|
||
.. code-block:: none | ||
/-> |Resource #1| | ||
|Generator #0| -> |Resource #0| -( | ||
\-> |Resource #2| | ||
There are two possible options at this stage: | ||
|
||
* Serve a copy of the same message to both ``Resource #1`` and ``Resource #2``; | ||
* Either serve a message to ``Resource #1`` or to ``Resource #2``. | ||
|
||
|
||
Automatic copy | ||
-------------- | ||
|
||
In case of serving a copy of the same message to both ``Resource #1`` and ``Resource #2``, the SimPype code would hence be: | ||
|
||
.. code-block:: python | ||
import simpype | ||
import random | ||
sim = simpype.Simulation(id = 'single') | ||
gen0 = sim.add_generator(id = 'gen0') | ||
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)} | ||
res0 = sim.add_resource(id = 'res0') | ||
res0.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
res1 = sim.add_resource(id = 'res1') | ||
res1.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
res2 = sim.add_resource(id = 'res2') | ||
res2.random['service'] = {0: lambda: random.expovariate(2.0)} | ||
p0 = sim.add_pipeline(gen0, res0, res1) | ||
p1 = sim.add_pipeline(gen0, res0, res2) | ||
pM = sim.merge_pipeline(p0, p1) | ||
sim.run(until = 5) | ||
Please note the use of :meth:`~simpype.simulation.Simulation.merge_pipeline`. This function merges multiple `pipelines` into a single one, thus creating the branching point. | ||
Withouth calling the :meth:`~simpype.simulation.Simulation.merge_pipeline` function, the only active `pipeline` would have been ``p1``. | ||
|
||
As it can be noticed in ``sim.log`` file, messages are automatically copied and served to both ``Resource #1`` and ``Resource #2`` after being served by ``Resource #0``: | ||
|
||
.. code-block:: none | ||
timestamp,message,seq_num,resource,event | ||
0.000000000,gen0,0,res0,pipe.default.in | ||
0.000000000,gen0,0,res0,pipe.default.out | ||
0.412762064,gen0,0,res0,resource.serve | ||
0.412762064,gen0,0,res2,pipe.default.in | ||
0.412762064,gen0,0,res1,pipe.default.in | ||
0.412762064,gen0,0,res2,pipe.default.out | ||
0.412762064,gen0,0,res1,pipe.default.out | ||
0.631472230,gen0,0,res1,resource.serve | ||
0.989221320,gen0,0,res2,resource.serve | ||
2.545794865,gen0,1,res0,pipe.default.in | ||
2.545794865,gen0,1,res0,pipe.default.out | ||
2.572402316,gen0,1,res0,resource.serve | ||
2.572402316,gen0,1,res2,pipe.default.in | ||
2.572402316,gen0,1,res1,pipe.default.in | ||
2.572402316,gen0,1,res2,pipe.default.out | ||
2.572402316,gen0,1,res1,pipe.default.out | ||
2.602942195,gen0,1,res1,resource.serve | ||
4.163453623,gen0,2,res0,pipe.default.in | ||
4.163453623,gen0,2,res0,pipe.default.out | ||
4.222865258,gen0,2,res0,resource.serve | ||
4.222865258,gen0,2,res2,pipe.default.in | ||
4.222865258,gen0,2,res1,pipe.default.in | ||
4.222865258,gen0,2,res1,pipe.default.out | ||
4.270038314,gen0,1,res2,resource.serve | ||
4.270038314,gen0,2,res2,pipe.default.out | ||
4.360461106,gen0,2,res2,resource.serve | ||
4.551208266,gen0,2,res1,resource.serve | ||
Next hop selection | ||
------------------ | ||
|
||
Please refer to :ref:`message_next` in :ref:`message` section to understand how the next hop of the messages can be dynamically changed. | ||
|
||
Miscellaneous | ||
============= | ||
|
||
:meth:`~simpype.simulation.Simulation.add_pipeline` admits both :class:`~simpype.resource.Resource` and :class:`~simpype.pipeline.Pipeline` objects as arguments as shown in this examples: | ||
|
||
.. code-block:: python | ||
import simpype | ||
sim = simpype.Simulation(id = 'single') | ||
gen0 = sim.add_generator(id = 'gen0') | ||
gen1 = sim.add_generator(id = 'gen1') | ||
res0 = sim.add_resource(id = 'res0') | ||
res1 = sim.add_resource(id = 'res1') | ||
res2 = sim.add_resource(id = 'res2') | ||
res3 = sim.add_resource(id = 'res3') | ||
res4 = sim.add_resource(id = 'res4') | ||
res5 = sim.add_resource(id = 'res5') | ||
res6 = sim.add_resource(id = 'res6') | ||
# Only resources | ||
p0 = sim.add_pipeline(res0, res1, res2) | ||
p1 = sim.add_pipeline(res3, res4, res5) | ||
# Mixed pipeline and resources | ||
p2 = sim.add_pipeline(gen0, p0) | ||
p3 = sim.add_pipeline(gen1, p1) | ||
p4 = sim.add_pipeline(p3, res6) | ||
# Only pipelines | ||
# Equivalent to sim.add_pipeline(res0, res1, res2, res3, res4, res5) | ||
p4 = sim.add_pipeline(p0, p1) | ||
Instead, :meth:`~simpype.simulation.Simulation.merge_pipeline` only admits :class:`~simpype.pipeline.Pipeline` objects as arguments. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters