@@ -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.
97103A medium range workstation (e.g. dual Intel Xeon E5-2620 2.40GHz CPU, 24
98104logical cores, 64GB RAM, local SSD drives) should be able to run all
99105examples 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
101108external (user) code. Also, instead of generating random image data, one
102109could, for example, concatenate actual image data into a set of NumPy arrays
103110and 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
632639Once the data source starts publishing images, they will be streamed through
633640all 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
637776This 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+
0 commit comments