{'fraction_non_alphanumeric': 0.03402269861286255, 'fraction_numerical': 0.009205548549810845, 'mean_word_length': 5.122760963557751, 'pattern_counts': {'":': 0, '<': 0, '<?xml version=': 0, '>': 0, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
NNStreamer: Stream Processing Paradigm for Neural Networks, Toward Efficient Development and Execution of On-Device AI Applications

We propose nnstreamer, a software system that handles neural networks as filters of stream pipelines, applying the stream processing paradigm to neural network applications. A new trend with the wide-spread of deep neural network applications is on-device AI; i.e., processing neural networks directly on mobile devices or edge/IoT devices instead of cloud servers. Emerging privacy issues, data transmission costs, and operational costs signifies the need for on-device AI especially when a huge number of devices with real-time data processing are deployed. Nnstreamer efficiently handles neural networks with complex data stream pipelines on devices, improving the overall performance significantly with minimal efforts. Besides, nnstreamer simplifies the neural network pipeline implementations and allows reusing off-shelf multimedia stream filters directly; thus it reduces the developmental costs significantly. Nnstreamer is already being deployed with a product releasing soon and is open source software applicable to a wide range of hardware architectures and software platforms.


Introduction
We have witnessed the wide spread of deep neural networks and their applications in the last decade. With ever growing computing power of embedded or edge devices, neural networks are being adopted to such devices, further assisted by AI accelerators [2,10,24,25,27,38,44,48]. In mobile phone industry, this trend has already become obvious enough to be adopted by major manufacturers including Samsung and Apple [2,44]. Running intelligence mechanisms such as deep neural networks directly in edge devices including consumer electronics is often called as on-device AI [37].
On-device AI is becoming more attractive to manufacturers because of the following three advantages: 1. Avoid data privacy and protection issues by keeping the data in user devices without uploading to cloud servers. 2. Reduce data transmission latency of high-bandwidth realtime stream data including live video feeds from cameras.
3. Reduce operating cost by off-loading computation to devices from servers. This cost is often neglected; it is, however, significant if billions of devices are to be deployed. We do not discuss the potential advantage, distributing workloads across edge devices, because it is not in the scope of this paper, but of future work.
On-device AI achieves such advantages by processing directly in the nodes where data exist so that data transmission is reduced and sensitive data are kept inside. However, on-device AI induces significant challenges. Normally, edge devices including mobile phones, TVs, home appliances, and IoT devices have limited computing power compared to workstations of conventional AI systems. In the meanwhile, ondevice AI applications often require short response time or high processing rates; e.g., AR Emoji [43], Animoji [3], and offline speech recognition and translations. Besides, on-device AI applications often use sensors incurring data stream with high bandwidth: video cameras.
On-device AI applications are becoming more complex, incurring even more challenges. A neural network may process multiple input streams and multiple neural networks may process streams simultaneously. Besides, networks may share input data, and an input of a network may be outputs of other networks. For example, inputs of a facial recognition and an emotion recognition may share outputs of an object recognition, saving computing resources. Composing systems with multiple networks allows to train smaller networks (more efficient) and to save training costs. However, stream pipelines for such systems may become highly complicated with interconnections of networks and sensors along with fluctuating latency and synchronization issues. Another challenge is that the topology of pipelines may be dynamic.
We have observed another issue in practice; developers may use their own neural network frameworks such as TensorFlow and Caffe or their own tweaked version of such frameworks. With stable releases of applications, we may enforce developers to share a framework or to provide independent binaries without the need for any neural network frameworks. However, for researching and prototyping, developers want to ex-periment with various frameworks while testers still need to execute the whole system integrated. Thus, we need to compose such complicated interactions between neural networks and sensors along with multiple neural network frameworks.
We apply the stream processing paradigm [46] to on-device AI systems. Our approach is to handle a neural network as a stream filter and then, to construct the system as a stream pipeline. We propose data standards for tensors and containers of tensors, which allow varying neural network frameworks in a pipeline along with conventional stream filters, conventional media types (video, audio, and text), and tensors. In order to construct complicated pipelines, we also propose various stream path manipulators with synchronization policies and tensor operators.
Handling stream pipelines efficiently along with various synchronization methods, filters, and data types has been a main topic of multimedia frameworks for decades. Among multimedia frameworks, we choose GStreamer [19] as the basis because GStreamer offers highly modular architecture, is fully open source software, and is compatible with various operating systems and hardware architectures. We can also reuse what GStreamer offers; hundreds of media processing plugins and developmental infrastructure. GStreamer has been widely deployed to desktop computing, video conferencing, broadcasting, and consumer electronics, even including televisions of Samsung and LG, where the multimedia performance is extremely critical.
An experimental product deployed to hospitals and households, which will be commercially released soon, is based on nnstreamer to process recognize events from multiple sensors. Product developers have significantly improved the performance and simplified the code, which have enabled to add more sensors and neural networks with inexpensive hardware and minimal developmental costs. Nnstreamer is open source software licensed with LGPL [16]. We plan to use nnstreamer for more products in the affiliation including robots and conventional consumer electronics.

Multimedia Stream Processing in Practice
Multimedia stream frameworks have recently evolved to process high quality audio and video with mobile phones and TVs whose computing resources are limited.
GStreamer [19] is the standard multimedia pipeline framework for Tizen and many Linux distributions. GStreamer provides APIs and utilities to construct stream pipelines for multimedia applications of various platforms including Linux, Android, Windows, iOS, and macOS. GStreamer is highly modular; every filter and path control is implemented as a plugin attached in run-time. GStreamer has been applied to various systems where multimedia performance matters. BBC uses GStreamer for their broadcasting system [11]. Samsung and LG use GStreamer as the multimedia engine of televisions. Centricular [14,15,23,49,50] releases GStreamer for TVs, set-top boxes, medical devices, in-vehicle infotainment, video-on-demand, and on-demand streaming solutions.
Another popular framework for Linux, FFmpeg [5] is not modular, but everything is built-in and has limited crossplatform support. Note that GStreamer can embed FFmpeg as yet another plugin.
StageFright [17] is the multimedia stream framework of Android. StageFright depends on Android services and, unlike GStreamer, is not flexible enough for other generic Linux distributions. Besides, it does not allow to construct arbitrary stream pipelines.
AVFoundation [4] is the multimedia stream framework of iOS and macOS. Along with Core ML, a machine learning framework, we may have AVFoundation as an input to Core ML; however, it is not supposed to write arbitrary pipelines with Core ML entities as stream filters.
DirectShow [9] is the multimedia framework of Windows. We cannot alter and use DirectShow or AVFoundation for the given purpose because it is proprietary software and supports proprietary platforms only.

Stream Processing in General
StreamCloud [21] has addressed the bottleneck of a single input node in a stream pipeline. StreamCloud provides a scalable and elastic stream framework to process large data input and execute pipeline elements in parallel transparently by splitting queries to allocate multiple nodes.
There are studies to help manage large data streams efficiently; however, they do not provide a general stream processing framework for neural networks and complex topology. Kumar [32] has proposed a network data stream analysis tool with higher accuracy and lower overhead than prior work. In order to reduce power consumption of edge devices receiving multimedia data streams, a client-side statistical prediction scheme [51] estimates the data patterns of incoming data streams and tries to make processors sleep longer. A fragmental proxy-caching scheme [47] tries to improve the quality of multimedia streams by adjusting caching units with finer granularity. Apache Flink [7] proposes a platform with a universal dataflow engine designed to perform both stream and batch analytics for processing streaming and batch data.
Applying streaming framework for GPUs fits well with general architectures of GPUs. GStream [54] provides a general-purpose and scalable data streaming framework for GPUs. Nvidia Tesla architecture [36] provides a flexible, programmable graphics and high-performance computing along with the Compute Unified Device Architecture (CUDA) platform. Jiangiang Dong [13] demonstrates how modern GPUs and CUDA can improve the performance of BIRCH, one of well-known clustering techniques for streaming data.

Stream Processing for Neural Networks
Nvidia DeepStream [28,40] provides neural network filters for GStreamer. DeepStream requires the input and output of a neural network to be conventional media stream. Other critical issues include: a) DeepStream is proprietary, b) it supports Nvidia hardware only, and c) general neural network frameworks cannot be applied directly.
In GStreamer Conference 2018, other approaches to apply neural networks to GStreamer are introduced as well. Intel [52] proposes an approach similar with DeepStream, sharing the same issues. Pexip [45] proposes to use OpenCV to apply neural networks as prior work [35]. This approach is the least invasive method; however, this cannot be applied to neural networks with arbitrary input and output streams or with general neural network frameworks. RidgeRun [26] has proposed an approach somewhat similar with nnstreamer. However, it does not support neural networks with arbitrary input and output streams or path controls and, according to the opened code [18], it is far from releases.
In contrast, we recognize "tensor" as a stream data type, not limiting streams to conventional media types (audio, video, and text), allowing outputs of neural networks to be inputs of other networks in general and stream path controls discussed in Chapter 4. We provide efficient and high-performing methods to process neural network pipelines in parallel with arbitrary frameworks, hardware, and platforms.

Approaches
We describe the approaches of nnstreamer with example pipelines showing the need for each component. Later in Chapter 4, we show how such components are implemented.

Basic Components
Neural network frameworks (NNFWs), such as Caffe and TensorFlow, allow developers to describe, train, and execute neural network models. NNFWs also provide execution environments along with hardware acceleration.
An NNFW filter, "Neural Network" in Figure 1, allows to attach a neural network model and its NNFW as a stream filter of a pipeline. Each NNFW filter may use its own NNFW. A stream sink transmits the results to external entities such as applications and visualization services.
We need a standard stream data format representing inputs and outputs of neural networks, called "tensor". With the standard, we may run different NNFWs in a single pipeline and implement common data and path manipulators, directing outputs of networks as inputs of others. Then, we need Converters converting media streams to "tensor" streams.
We need preprocessors for tensors and media streams. A neural network input may have strict requirements; e.g., AlexNet [31] requires a normalized tensor of 3 x 224 x 224.
To meet such requirements, we need various preprocessors including conventional media filters (e.g., Video Decode & Scale in Figure 1) and tensor operators (e.g., Normalize & Transpose in Figure 1.)

Isodimensional Stream Path Control
In Figure 2, we show three dimension-preserving stream path controls. Rectangles with dotted lines represent sets of filters. Shaded rectangles show data formats. Thick arrows are streams where each frame has multiple instances of "tensor"s.
Tee allows multiple paths to share same data.
Mux multiplexes streams into a single stream having multiple instances of "tensor"s in each frame, referred as "tensors". In the figure, properties of "tensors" are denoted with curly brackets. Because Mux has a single output stream, its input streams are synchronized by Mux, which provides multiple synchronization policies. For example, "slowest" synchronizes with the slowest input, "base" synchronizes with a specific input, and "fastest" generates an output whenever an input is available. With "base" or "fastest", Mux may need to reuse input frames from a slower stream. For example, in Figure 2, if the policy is "fastest", previous frames from Infra-Red may be reused to meet 60 Hz.
Mux needs to look at time-stamps of incoming stream, stamped by sensors. Mux needs to choose each frame from the input streams with the closest time-stamp. For example, if frames of time-stamps, {14, 30, 49}, are available from Infra-Red and {29} from RGB Camera has just arrived, Mux chooses 30 unless a user specifies otherwise.
Demux demultiplexes an incoming "tensors" stream into multiple "tensor" streams as shown in the figure. We do not need synchronization mechanisms for Demux.
Recurrent neural networks (RNN) are popular for handwriting recognition and speech recognition applications. If a recurrence occurs in a single element of a pipeline without affecting other elements, referred as an internal recurrence, it is not exposed to its stream pipeline. However, as shown in Figure 3, if the recurrence affects and is visible to the pipeline, referred as an external recurrence, we need Recurrence Helper. Recurrent Helper resolves the bootstrapping issue; the output of Model 2 in the figure is not available at the start, which, in turn, makes the input of Model 1 not available and blocks the the whole pipeline. Besides, if we have meta data streams flowing back and forth as in GStreamer pipelines for QoS, cyclic paths can be prohibited; Recurrence Helper should resolve such conflicts as well.

Non-isodimensional Stream Path Control
Non-isodimensional stream path controls may alter the dimensions of tensors. Figure 4 shows two of such controls, Merge and Split. As shown in the figure, the dimension being merged or splited needs to be specified. Merge needs The long short-term memory (LSTM) is popular for object tracking, machine translation, image captioning, and various applications. In order to generate inputs for LSTM, we need to aggregate multiple frames of a stream into a single frame: Aggregator in Figure 5. Aggregator merges frames temporally while Mux and Merge merges frames spatially. Figure 5 shows an LSTM model processing 10 recent frames simultaneously. Similar configuration can be seen in activity recognition sensors [33], which use nnstreamer, in Chapter 5.1.

Other Requirements
From the beginning, we have been cooperating with potential product developers, who have requested the followings: • Dynamic flow control to enable and disable neural networks quickly: Valve in Figure 6. • Change stream sources dynamically and easily in case of

Implementation
Nnstreamer is not an experimental prototype, but software with imminent schedule and pressure of commercialization; thus, it should be ready for tiring quality assurance processes. Some of authors have been working for the autonomous vehicle project, where the need for applying stream processing paradigm has emerged. The vehicle has a lot of heterogeneous sensors and neural networks inter-connected with complex topology, transmitting large data streams, and processed simultaneously. The huge data transmission overhead between nodes had been the core cause requiring nnstreamer. Then, managing complex and ever-changing data paths, allowing different NNFWs, and parallelism have been incurring the need for nnstreamer.
Spinning off this project from the autonomous vehicle project, we have discovered that such needs are common: robots, manufacturing plants, and consumer electronics. As a result, nnstreamer has already had a lot of potential applications in the affiliation from the beginning, which enforces to release partial but practical solutions quickly.
Don't reinvent the wheel, just realign it. -Anthony J. D'Angelo If we implement yet another stream framework, it would take too much time and effort. Fortunately, a stable, widelydeployed, and open-source stream framework with rich features already exists: GStreamer. GStreamer is a multimedia stream framework battle-tested with a lot of services including BBC [11] and consumer electronics including televisions (both Samsung and LG), where multimedia performance is critical. Releasing large number of products, a lot of vendors have made sure GStreamer is reliable, high-performing, and rich-featured. Tizen, which runs a wide range of devices including most types of consumer electronics [22], also has GStreamer as its standard multimedia engine. As a result, most of requirements in Chapter 3.4 and the above are already satisfied by GStreamer.
We only need to "realign" GStreamer for neural networks and their applications. We implement what GStreamer is missing: the features described in Chapter 3 except for "Tee", "Valve", "Switch", "Queue", and conventional media filters.

Standard Stream Data Types
We have designed two new data types for GStreamer, "other/tensor" and "other/tensors", representing a stream of a multidimensional array and a stream of a container of multiple instances of such arrays, respectively. The definitions of both stream data types in the following paragraphs show how each frame instance of a stream appears. Unlike other approaches [26,28,45,52], neural networks in nnstreamer do not use conventional media types, but use the standard defined above because of the advantages mentioned in Chapter 3.1. For demonstrations, we may assume that the neural network output is a video stream; however, if an application uses the output, it usually wants the raw data, not the rasterized video frames. More critically, if there is another neural network using the output as its input, conventional media types do not fit often; e.g., an array of label probabilities is neither audio or video. Besides, a neural network may require inputs preprocessed in the form not compatible with conventional media types or the input itself might be not compatible with conventional media types. Therefore, standard tensor data stream types are critically required.
• tensor_converter converts audio, video, text, or arbitrary binary streams to other/tensor streams. • tensor_decoder converts other/tensor(s) to video or text stream with assigned sub-plugins. • tensor_filter invokes a neural network model with the given model path and NNFW name. • tensor_transform applies various operators to tensors including typecast, add, mul, transpose, and normalize. For faster processing, it supports SIMD instructions and multiple operators in a single filter. • tensor_mux, tensor_demux, tensor_merge, ten-sor_split, and tensor_aggregator support tensor stream path controls. • tensor_reposink and tensor_reposrc implement Recurrent Helper of Figure 3. In a pipeline of the figure, ten-sor_reposink is attached to "Tee" and tensor_reposrc is attached to the second "Mux", cutting the cycle in the pipeline. There is a shared repository for the two elements to transmit tensors without GStreamaer stream paths. There are two elements allowing adding user created features in run-time: tensor_filter and tensor_decoder. Figure 7 shows how user created neural networks and decoders can be attached to tensor_filter and tensor_decoder in a pipeline. In the figure, "make3DBoxes.so" is a neural network without NNFWs and "3dboxdraw.so" is a user decoder sub-plugin, decoding results of "make3DBoxes.so" to video streams. If the tensor_filter element has "mode=tensorflow-lite" and "model=x.tflite", TensorFlow-lite processes "x.tflite". Nnstreamer provides APIs to implement sub-plugins. And sub-plugins can be implemented and attached in run-time except for adding a new NNFW to tensor_filter, which is to be supported with later releases.
GStreamer supports various programming languages, hardware architectures, and operating systems. As a plugin of GStreamer, we ensure the compatibility by writing C89 code and using only the libraries GStreamer uses except for detachable sub-plugins and test cases. APIs are available for C, C++, .NET, Java, Python, Rust, Perl, Qt, Haskell, D, Guile, Ruby, and Vala. It supports Linux, Android, Windows, Mac OS X, iOS, BSD, Unix, Solaris, and Symbian along with x86, ARM, MIPS, SPARC and PowerPC.
We can reuse a wide range of stream analysis tools including profilers, tracers, test suites, and debuggers from GStreamer. Figure 8 is a screenshot of a tool analyzing a nnstreamer pipeline. We heavily depend on such tools to optimize or to fix bugs of commercialization projects.

Evaluations
We experiment with nnstreamer 0.1.0-1rc1 in devices described in Table 1 for two applications, an activity recognition sensor (ARS) based on [33] and Multi-Task Cascaded Convolutional Networks (MTCNN) [12,53]. Device A, Artik 530s, is the computing engine of ARS consisting of a dynamic vision sensor (DVS) [6] and an ultra-wideband (UWB) [8] sensor. Device B, Odroid-XU4, is an inexpensive development board with the SoC of Samsung Galaxy S5. Device A and B represent mid to low-end embedded devices. Device C is a development board with Exynos 8890 used by Samsung Galaxy S7 and automotive industry [42]. This represents high-end embedded devices. Device D is a desktop PC. We do not use GPUs in the experiments.
For ARS, we use Device A and compare an nnstreamer pipeline against a conventional implementation as the control. For MTCNN, we use Device B, C, and D and compare an nnstreamer pipeline against ROS [41], popular middleware for robotics projects, implementation as the control. The control represents the product development before nnstreamer.
For both applications, nnstreamer improves the performance significantly. The implementation is significantly simplified along with the improved code quality as well.

Experiments with ARS
ARS is a product experimentally deployed to households and public facilities and will be commercially available soon. We have applied nnstreamer to ARS to simplify the implementation, to improve the performance, and to add more sensors and features. Because the hardware, A in Table 1, has limited resources, processing multiple neural networks and streams is challenging. To evaluate the performance with the exactly same data, we use preprocessed input data stored as a file. Figure 9 describes algorithms for ARS and Figure 10 shows the nnstreamer pipeline for ARS. A in Figure 9 is the initial CNN-based DVS processing algorithm [33], where each instance of CNN accepts 8 consecutive images with offsets of 4 frames. Then, it detects activity events by applying the argument max operator to 6 latest CNN results. B improves A by adding another neural network, LSTM, instead of the simple argument max. Both A and B use NEON SIMD to accelerate for both nnstreamer and conventional implementations. They are attached as custom sub-plugins of tensor_filter. C is a CNN-based neural network implemented with TensorFlowlite processing 75 consecutive frames from two standardized streams from UWB, which outputs two streams. Table 2 compares the nnstreamer implementation and Control, which implements ARS with Python and NumPy computation library. The last column shows the performance improvement achieved by nnstreamer with geometric means; i.e., how much CPU time or memory is saved by nnstreamer or how much faster is nnstreamer than Control.  Row 1 shows the total lines of codes except for neural network models, which indirectly suggests that nnstreamer may lower the developmental costs. Both Control and nnstreamer share C binaries of neural network models, A and B. Nnstreamer C codes, A (169) and B (186), are trivial wrappers for the models based on the nnstreamer template. Control Python codes, A (364) and B (359), handle pipelines and threads, which are replaced by a single-line shell script with nnstreamer described in the last paragraph of this chapter. Surprisingly, nnstreamer has taken few days of a single developer, with no known bugs, to implement what Control has taken a few weeks of developers along with known bugs.
Other rows in Table 2 evaluate the performance. Row 2 suggests that nnstreamer incurs less memory copies, which is critical for embedded devices with low memory throughput.
Row 3 suggests that both implementations try to exploit parallelism with multi-threading. Row 4, 5, and 6 show the CPU usage (resource consumption), output frames-persecond (throughput), and the efficiency (throughput / resource consumption) respectively in case we have unlimited input rate; i.e., push as many input frames as stream filters process. They show that nnstreamer implementation is far more efficient (75.0 %) and has higher throughput (65.5 %) although nnstreamer has incurred significantly less time and effort of developers. Note that the output rate may be slower than the input rate because Aggregator aggregates multiple frames.
The efficiency of real product is shown with Row 7; nnstreamer saves 43.5 % of CPU workloads in average. Note that nnstreamer takes only a quarter of CPU time compared to Control to process UWB. With higher efficiency, nnstreamer enables inexpensive devices to process more sensors and neural networks with reduced developmental costs.
Nnstreamer does not incur memory-copy for inter-filter data transmissions. Even with Tee, there is no memory-copy between a source and sinks of the Tee unless there is an inplace operation-writing the output directly to the input bufferin a sink. Therefore, as long as there are no rogue nnstreamer custom filters, memory-copy operations are expected to be minimized while developing such optimized but complex pipelines with the conventional method is not so trivial.
Nnstreamer promotes parallelism with minimal developmental efforts. The stream pipeline paradigm allows to distribute workloads to multiple elements even if there is a single stream pipeline. Functional parallelism is promoted by processing elements or sub-pipelines in different processors with less efforts; the short code in the last paragraph of this chapter is sufficient. Besides, efficiency is further promoted by regulating processing rates; e.g., a producer will not process faster Figure 10: ARS Pipeline. The top half, B of Figure 9, processes DVS. The bottom half, C of FIgure 9, processes UWB.    Figure 11 shows resource utilization over time with unlimited input rates. The time on x-axis is normalized so that both cases are completed at the end of x-axis. Both CPU Usage and # Threads suggest that nnstreamer not only incurs less workloads, but also distributes workloads more evenly over time. Memory, an expensive resource for embedded devices, is significantly saved by nnstreamer as well: 39 vs 17 MiB.
The major barrier to using complex pipelines with multiple sensors and neural networks has been the difficulties of implementing the pipeline topology with data protections, processing rate regulations, and efficient data transfers. After the demonstration of the nnstreamer pipeline (Figure 10), ARS developers have abandoned their implementations and started using nnstreamer for its simplicity and efficiency. The following code is a shell script implementing the whole pipeline, which enables to test and modify it quickly along with offthe-shelf GStreamer debugging and profiling tools.

Experiments with MTCNN
We evaluate nnstreamer with MTCNN [12,53] because MTCNN has a complex pipeline topology and real life use cases for many applications; it detects faces and their alignments in a video frame. We compare the nnstreamer and ROS [41] implementations with Full-HD videos in various devices, B (low/mid-end embedded), C (high-end embedded), and D (PC) described in Table 1. Figure 12 shows the nnstreamer pipeline topology of MTCNN, which has two large sub-pipelines. One, the top dotted rectangle in Figure 12, composites (GStreamer/cairooverlay) and displays (GStreamer/ximagesink) the live camera feed and the result of the other sub-pipeline. The other sub-pipeline, representing the MTCNN algorithm, is divided into three stages: Proposal Network (P-Net), Refine Network (R-Net), and Output Network (O-Net). MTCNN uses a cascade of convolutional networks in an image pyramid. In other words, each input frame is scaled down to various sizes comprising an image pyramid in P-Net, where each size is represented by a layer. Therefore, P-Net Stage has multiple streams (one for each layer) and an instance of P-Net including preand post-processing is processed in each stream. Then, in the order of P-Net, R-Net, and O-Net, each stage is linked and processed, which is post-processed by Non-Maximum Suppression (NMS) and Bounding-Box Regression (BBR).
As shown in Figure 12, MTCNN requires image processors: scale, transpose, color space conversion, and data type conversion. Before applying nnstreamer, ROS developers have relied on OpenCV for image processing, requiring additional time and effort. Besides, the performance enhancement with parallelism has not been successful with the ROS implementation. To promote parallelism, first, they have tried to adopt the pipe and filter style [39] (implementing the stream pipeline paradigm) by dividing the stages as ROS nodes. However, adding more ROS nodes have increased the message overheads (we use Full-HD videos!) and deteriorated the throughput significantly. As a result, they have abandoned the pipe and filter style despite of the observation that we can significantly accelerate P-Net by exploiting parallelism. Applying parallelization libraries such as POSIX Threads to such complex architecture is not a trivial task for many AI project developers, either.
As in ARS, exploiting parallelism has been highly simplified and trivial with nnstreamer. The following code implements a single layer of P-Net 2 , where gst_parse_launch [20] constructs a pipeline, which can be a part of a larger pipeline.
gchar *desc1 = g_strdup_printf ( "queue ! videoscale ! " "video/x-raw,width=%d,height=%d ! " "tensor_converter ! " "tensor_transform mode=arithmetic " "option=typecast:float32,add:-127.5," "mul:0078125 ! " "tensor_transform mode=transpose" "option=0:2:1:3 ! " "tensor_filter framework=tflite " "model=./pnet.tflite ! " "tensor_filter framework=custom " "model=./pnet_pb.so", width1, height1); GstElement *layer1 = gst_parse_launch ( desc1, NULL); The above code of a single P-Net layer processes a given size in the image pyramid (width1 × height1), and is linked to Tee and Mux as shown in Figure 12. Then, each layer is executed as a thread, enabling functional parallelism for P-Net Stage, synchronized by the Mux between P-Net Stage and R-Net Stage. The Queue between the two Tees before P-Net Stage allows to drop frames for MTCNN if MTCNN cannot follow the frame rates of incoming video while not affecting the live feed displayed to Video Sink. For proper and optimized behaviors, we also need to configure queueing policies appropriately; i.e., how buffers are leaked and how many buffers may wait in a queue. Table 3 shows the total lines of codes and the number of effective threads and their decompositions. Control C++ codes has three ROS nodes: Input, MTCNN (70 LOC), and Display (1574 LOC). The ROS-MTCNN node is a single threaded implementation that does not require any inter-node data transmissions. We do not count LOC of Input because we reuse open source code for Input. We do not count threads of ROS core and middleware as they are not effective for parallel executions of MTCNN.
Nnstreamer C/C++ codes have a pipeline (Display (51 LOC) and Pipeline (904 LOC) sub-pipelines) and tensor_filter custom sub-plugins (1004 LOC). Displays of both implementation have the same functionality and MTCNN of ROS is equivalent to Pipeline and Custom Filters of nnstreamer. Pipeline LOC counts the stream pipeline description of the MTCNN sub-pipeline. As with ARS, the two custom subplugins are the trivial wrappers based on the nnstreamer template. Compared to Control (1644), nnstreamer (1959) has more LOC. However, considering that only a few more trivial codes are required (333 LOC) to achieve parallelism in such a complex pipeline, the result suggests that nnstreamer allows to make development cost lower with higher efficiency and better quality than the conventional method. Besides, compared to Control, nnstreamer developers have added a lot more features including extensive error handling, appropriate frame dropping, dynamic number of layers, and dynamic in-    put video sizes and rates along with the reliability to handle camera inputs continuously. Control consists of 3 single-threaded ROS nodes, incurring 3 effective threads. Besides, most computation happens with MTCNN node, which implies that there is little effective parallelism with Control except for OpenCV functions. Nnstreamer has much higher number of effective threads (19) although we do not write any multi-threaded codes explicitly. Particularly, P-Net Stage has 14 threads automatically and dynamically (according to the size of input images) created by the framework, which implies that nnstreamer highly promotes parallelism easily and efficiently. Table 4 compares the performance of the two implementa-tions in various devices. Row 1 shows the end-to-end latency between the sensor input to the display output for a single image frame with the input rate (1 FPS) slow enough to have a single frame in a pipeline. Moreover, Figure 13 shows the decompositions of latency, which suggests that most of the latency reduction comes from P-Net. More specifically, the performance, defined as 1/latency, of P-Net Stage is improved by 49 Row 2 shows the output rates from 30 frames per second input of a Full-HD camera, processing Full-HD images with MTCNN. This allows pipelined data-parallelism, allowing processing different frames in filters. As a result, the performance improvement by nnstreamer is more significant: 83.21% (up to 171.62% for Device B.) Note that the output frame rate is slower than the input rate; thus, input frames are often dropped in front of P-Net with the nnstreamer implementation. However, the frames for display compositing are not dropped and the final display output keeps showing 30   Analysing the effect of functional parallelism, we have mentioned that the performance of R-Net and O-Net have not been improved by the nnstreamer implementation; it is rather deteriorated as shown in Figure 13. This is mainly because the two custom tensor_filter sub-plugins (N/B/I) in R-Net Stage incur memory copies of Full-HD frames in order to generate small image patches for R-Net and O-Net. On the other hand, Control uses a global variable to avoid memory copies for the image patch generation. As a result, with devices of higher memory bandwidth, the performance deterioration in R-Net is reduced; 16.21 ms for Device D, 33.66 ms for Device C, and 91.98 ms for Device B.
The nnstreamer implementation uses more CPU time to process faster. Because, output rates of both implementations have not reached the input rates, it would be better to consume more CPU time to process more frames per second. The memory consumption is higher with the nnstreamer implementation although it is within the acceptable limit. However, MTCNN-nnstreamer consumes more than 10 times of the memory consumed by ARS-nnstreamer, which implies that the pipeline implementation is wasting the memory, probably by queues: we have attached too many queues that may store Full-HD frames. For the image pyramid of MTCNN, it would be significantly efficient (for both CPU and memory) if we write a custom tensor_filter sub-plugin that generates multiple layers of images directly from an input stream.

Future Work
The following filters and features are in our short-term plan.
• tensor_ros_sink/src allow to exchange tensor streams with Robot OS (ROS) [41] nodes for ROS1 and ROS2. • tensor_save/load store tensor streams as files and load the files as tensor streams. • tensor_protobuf_sink/src allow to use Google's protobuf as a transport layer. • tensor_source accepts sensor data not compatible with GStreamer including LIDARs, RADARs, and others. • More NNFWs for tensor_filter: Caffe/Caffe2 and Keras. • Make other/tensor(s) a GStreamer standard type. There are long-term plans as well. First, we will apply the actor model [1] to make pipelines fully-distributed. Robotics projects often incur multiple computers with different roles, which requires filters of a pipeline distributed across multiple computers.
Another long-term plan is to allow retraining a pipeline directly solely on device. Federated Learning [30] allows to update models by retraining models in cloud based on the data gathered from edges; however, it is difficult to apply Federated Learning if protected privacy data is required for training and the data cannot be transmitted. Therefore, we are sometimes required to retrain neural networks directly in edge devices.

Conclusion
From the experiences of on-device AI applications, our conjecture is that applying stream processing paradigm can greatly improve the performance, and the implementation productivity. By implementing and deploying nnstreamer, we show that such improvements can be achieved successfully for ondevice AI applications: higher throughput and easier implementation with more features and higher code quality. As a side effect, traditional multimedia developers can now employ arbitrary neural network models in their own multimedia pipelines with nnstreamer.
This work is being deployed for commercial products by the affiliation of the authors. We plan to adopt nnstreamer to a wide range of devices by making it the standard intelligence framework of Tizen OS. This work is compatible and tested with many software platforms including Tizen, Android, and Ubuntu and is open source software licensed in LGPL.

Availability
Nnstreamer is open source software developed in a public GitHub repository. Ubuntu 16.04 and 18.04 users may use PPA to install nnstreamer and keep it updated: $ sudo add-apt-repository ppa:nnstreamer/ppa $ sudo apt-get update $ sudo apt-get install nnstreamer