Skip to content

Commit ce8688a

Browse files
committed
Merge branch 'master' into release/5.1
2 parents c3fea58 + 6826a7b commit ce8688a

File tree

4 files changed

+229
-2
lines changed

4 files changed

+229
-2
lines changed
218 KB
Loading
346 KB
Loading

documentation/streamingFramework.md

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class UserDataProcessor:
2020

2121
def __init__(self, configDict={}):
2222
...
23+
# The following will be set after processor gets instantiated.
24+
self.processorId = None
25+
self.pvaServer = None
26+
self.outputChannel = None
27+
self.objectIdField = None
28+
self.metadataQueueMap = {}
2329

2430
# Method called at start
2531
def start(self):
@@ -97,7 +103,8 @@ might have to be tweaked in order for examples to run without lost frames.
97103
A medium range workstation (e.g. dual Intel Xeon E5-2620 2.40GHz CPU, 24
98104
logical cores, 64GB RAM, local SSD drives) should be able to run all
99105
examples shown here without any issues. Note that some commands use
100-
[sample AD image processor](../examples/hpcAdImageProcessorExample.py) as
106+
[sample AD image processor](../examples/hpcAdImageProcessorExample.py) or
107+
[sample AD metadata processor](../examples/hpcAdMetadataProcessorExample.py) as
101108
external (user) code. Also, instead of generating random image data, one
102109
could, for example, concatenate actual image data into a set of NumPy arrays
103110
and pass that file into the pvapy-ad-sim-server command using the
@@ -632,6 +639,138 @@ $ pvapy-ad-sim-server -cn ad:image -nx 128 -ny 128 -dt uint8 -fps 2000 -rt 60 -r
632639
Once the data source starts publishing images, they will be streamed through
633640
all the components of the system, and saved into the designated output folder.
634641

642+
### Metadata Handling with Data Collector
643+
644+
In many cases images need to be associated with with various pieces of metadata (e.g., position information)
645+
before processing. The streaming framework allows one to receive PV updates from any number of metadata channels
646+
(CA or PVA), which are made available to the user processing module as a dictionary of metadata
647+
channel names/PvObject queues.
648+
649+
<p align="center">
650+
<img alt="Metadata Handling with Data Collector" src="images/StreamingFrameworkMetadataHandlingDataCollector.jpg">
651+
</p>
652+
653+
This example uses [sample AD metadata processor](../examples/hpcAdMetadataProcessorExample.py) module which is capable of
654+
associating images with available metadata based on their timestamp comparison, and producing NtNdArray objects
655+
that contain additional metadata attributes. To see how it works,
656+
download the sample metadata processor and start data collector on terminal 1
657+
using the following command:
658+
659+
```sh
660+
$ pvapy-hpc-collector \
661+
--collector-id 1 \
662+
--producer-id-list 1 \
663+
--input-channel pvapy:image \
664+
--control-channel collector:*:control \
665+
--status-channel collector:*:status \
666+
--output-channel collector:*:output \
667+
--processor-file /path/to/hpcAdMetadataProcessorExample.py \
668+
--processor-class HpcAdMetadataProcessor \
669+
--report-period 10 \
670+
--server-queue-size 100 \
671+
--collector-cache-size 100 \
672+
--monitor-queue-size 1000 \
673+
--metadata-channels pva://x,pva://y,pva://z
674+
```
675+
676+
On terminal 2 generate test images on channel 'pvapy:image' and PVA metadata on channels 'x', 'y', and 'z':
677+
678+
```sh
679+
$ pvapy-ad-sim-server \
680+
-cn pvapy:image -nx 128 -ny 128 -fps 100 -rp 100 -rt 60 \
681+
-mpv pva://x,pva://y,pva://z
682+
```
683+
684+
After image generation starts, on terminal 3 inspect both the original and processed images. The output channel
685+
should contain the original image data plus values for x, y, and z attributes:
686+
687+
```sh
688+
$ pvget pvapy:image # original image, no metadata
689+
$ pvget collector:1:output # should contain x,y,z metadata
690+
```
691+
692+
Note that the generated PVA metadata channels have a structure containing value and timestamp:
693+
694+
```sh
695+
$ pvinfo x
696+
x
697+
Server: ...
698+
Type:
699+
structure
700+
double value
701+
time_t timeStamp
702+
long secondsPastEpoch
703+
int nanoseconds
704+
int userTag
705+
```
706+
707+
Since retrieving PVs from CA IOCs results in the same channel structure as in the above example,
708+
the sample metadata processor works with either CA or PVA metadata channels. This can be verified
709+
by replacing the AD simulation server command with the following:
710+
711+
```sh
712+
$ EPICS_DB_INCLUDE_PATH=/path/to/epics-base/dbd pvapy-ad-sim-server \
713+
-cn pvapy:image -nx 128 -ny 128 -fps 1 -rt 60 \
714+
-mpv x,y,z
715+
```
716+
717+
This command will start CA IOC and generate CA metadata channels 'x', 'y', and 'z'.
718+
Note that it requires path to the EPICS Base dbd folder. For example, if you are using PvaPy
719+
conda package, this folder would be located at '/path/to/conda/envs/env-name/opt/epics/dbd'.
720+
721+
### Metadata Handling with Distributed Consumers
722+
723+
Distributing metadata processing should allow one to handle higher frame rates. In this example
724+
we also use mirror server for all image and metadata channels.
725+
726+
<p align="center">
727+
<img alt="Metadata Handling with Distributed Consumers" src="images/StreamingFrameworkMetadataHandlingDataConsumers.jpg">
728+
</p>
729+
730+
On terminal 1, start 4 metadata processors listening on 'pvapy:image' channel:
731+
732+
```sh
733+
$ pvapy-hpc-consumer \
734+
--consumer-id 1 \
735+
--n-consumers 4 \
736+
--input-channel pvapy:image \
737+
--control-channel consumer:*:control \
738+
--status-channel consumer:*:status \
739+
--output-channel consumer:*:output \
740+
--processor-file /path/to/hpcAdMetadataProcessorExample.py \
741+
--processor-class HpcAdMetadataProcessor \
742+
--processor-args '{"timestampTolerance" : 0.00025}' \
743+
--report-period 10 \
744+
--server-queue-size 2000 \
745+
--accumulate-objects 10 \
746+
--monitor-queue-size 0 \
747+
--distributor-updates 1 \
748+
--metadata-channels pva://pvapy:x,pva://pvapy:y,pva://pvapy:z
749+
```
750+
751+
Each consumer will accumulate 10 images in the queue before processing them, in order to make sure
752+
all metadata arrives before it is needed.
753+
754+
On terminal 2, start mirror server mapping all channels:
755+
756+
```sh
757+
$ pvapy-mirror-server \
758+
--channel-map "(pvapy:image,ad:image,pva,1000),(pvapy:x,ad:x,pva,1000),(pvapy:y,ad:y,pva,1000),(pvapy:z,ad:z,pva,1000)"
759+
```
760+
761+
On terminal 3 generate images and metadata:
762+
763+
```sh
764+
$ pvapy-ad-sim-server \
765+
-cn ad:image -nx 128 -ny 128 -fps 2000 -rp 2000 -rt 60 \
766+
-mpv pva://ad:x,pva://ad:y,pva://ad:z
767+
```
768+
769+
Processing speed gains are not linear when compared to the single consumer case, because
770+
each consumer receives alternate set of images and all metadata values, and hence some
771+
metadata values will have to be discarded. This will be reflected in the metadata
772+
processor statistics.
773+
635774
### Data Encryption
636775

637776
This example illustrates how data can be encrypted in the first processing
@@ -712,3 +851,91 @@ $ pvget enc:1:output # encrypted data
712851
$ pvget dec:1:output # decrypted (raw) data
713852
$ pvget proc:1:output # processed image
714853
```
854+
855+
## Performance Testing
856+
857+
All tests described in this section have been performed with PvaPy version
858+
5.1.0 (Python 3.9 conda package) on a 64-bit linux machine with 96 logical cores (Intel Xeon
859+
Gold 6342 CPU with hyperthreading enabled) running at 3.5 GHz, and
860+
with 2TB of RAM. Image server and all consumers were running on the
861+
same machine.
862+
863+
### Throughput Tests
864+
865+
In order to asses how much data can be pushed through the framework we
866+
ran a series of tests using the [base system user processor] (../pvapy/hpc/userDataProcessor.py)
867+
that does not manipulate image and hence does not generate any additional
868+
load on the test machine.
869+
870+
On terminal 1, we used the following command to spawn 1 or more
871+
consumer processes:
872+
873+
```sh
874+
$ pvapy-hpc-consumer \
875+
--input-channel pvapy:image \
876+
--control-channel consumer:*:control \
877+
--status-channel consumer:*:status \
878+
--output-channel consumer:*:output \
879+
--processor-class pvapy.hpc.userDataProcessor.UserDataProcessor \
880+
--report-period 10 \
881+
--server-queue-size SERVER_QUEUE_SIZE \
882+
--n-consumers N_CONSUMERS \
883+
[--distributor-updates 1]
884+
```
885+
886+
Server queue size varied according to the test image size.
887+
Whenever we used multiple consumers (N_CONSUMERS > 1) data distributor was
888+
turned on using the '--distributor-updates 1' option. For a single consumer
889+
this option was left out.
890+
891+
On terminal 2 images were generated for 60 seconds using the following command:
892+
893+
```sh
894+
$ pvapy-ad-sim-server \
895+
-cn ad:image -nf 100 -dt uint8 -rt 60 \
896+
-nx FRAME_SIZE -ny FRAME_SIZE -fps FRAME_RATE -rp FRAME_RATE
897+
```
898+
899+
The above command was able to reliably generate images at stable rates
900+
of up to 20 KHz. Going beyond that number, the resulting frame output frame rate varied
901+
too much (more than a few Hz).
902+
903+
A given test was deemed successful if no frames were
904+
missed during the 60 second server runtime. Results for the maximum
905+
simulated detector rate that image consumers were able to sustain
906+
without missing any frames are shown below:
907+
908+
* Image size: 4096 x 4096 (uint8, 16.78 MB); Server queue size: 100
909+
910+
| Consumers | Frames/second | Frames/second/consumer | Frames/minute | Data rate/consumer | Total data rate |
911+
| ---: | ---: | ---: | ---: | ---: | ---: |
912+
| 1 | 150 | 150 | 9000 | 2.52 GBps | 2.52 GBps |
913+
| 4 | 600 | 150 | 36000 | 2.52 GBps | 10.07 GBps |
914+
| 8 | 1000 | 125 | 60000 | 2.10 GBps | 16.78 GBps |
915+
| 10 | 1200 | 120 | 72000 | 2.01 GBps | 20.13 GBps |
916+
917+
* Image size: 2048 x 2048 (uint8, 4.19 MB); Server queue size: 200
918+
919+
| Consumers | Frames/second | Frames/second/consumer | Frames/minute | Data rate/consumer | Total data rate |
920+
| ---: | ---: | ---: | ---: | ---: | ---: |
921+
| 1 | 700 | 700 | 42000 | 2.94 GBps | 2.94 GBps |
922+
| 4 | 2600 | 650 | 156000 | 2.73 GBps | 10.91 GBps |
923+
| 8 | 4000 | 500 | 240000 | 2.10 GBps | 16.78 GBps |
924+
| 10 | 4500 | 450 | 270000 | 1.89 GBps | 18.88 GBps |
925+
926+
* Image size: 1024 x 1024 (uint8, 1.05 MB); Server queue size: 500
927+
928+
| Consumers | Frames/second | Frames/second/consumer | Frames/minute | Data rate/consumer | Total data rate |
929+
| ---: | ---: | ---: | ---: | ---: | ---: |
930+
| 1 | 3200 | 3200 | 192000 | 3.36 GBps | 3.36 GBps |
931+
| 4 | 10000 | 2500 | 600000 | 2.62 GBps | 10.49 GBps |
932+
| 8 | 12000 | 1500 | 720000 | 1.57 GBps | 12.58 GBps |
933+
| 10 | 14000 | 1400 | 840000 | 1.47 GBps | 14.68 GBps |
934+
935+
* Image size: 512 x 512 (uint8, 0.26 MB); Server queue size: 1000
936+
937+
| Consumers | Frames/second | Frames/second/consumer | Frames/minute | Data rate/consumer | Total data rate |
938+
| ---: | ---: | ---: | ---: | ---: | ---: |
939+
| 1 | 10000 | 10000 | 600000 | 2.62 GBps | 2.62 GBps |
940+
| 4 | 20000 | 5000 | 1200000 | 1.31 GBps | 5.24 GBps |
941+

pvapy/hpc/userDataProcessor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, configDict={}):
2727
'''
2828
self.logger = LoggingManager.getLogger(self.__class__.__name__)
2929

30-
# The following will be set after object gets created.
30+
# The following will be set after processor gets instantiated.
3131
self.processorId = None
3232
self.pvaServer = None
3333
self.outputChannel = None

0 commit comments

Comments
 (0)