Skip to content

Part 4: Accelerate your app

Estimated reading time: 13 minutes

In this section, we will teach you how to create your own simple application to invoke the accelerators implemented in Part 3.

For the purpose of this tutorial, we will create an application using the C++ API, though you can also check the programming interfaces for Java, Scala and Python (coming soon).

Prerequisites-

  1. Coral API (coral-api) package should be installed in your system. Complete instructions on how to install it are provided here.

Introduction-

Let's assume we are willing to add the respective elements of two arrays and store their sum in a third array. Afterwards, we want to do element-wise subtraction of the same arrays and store the result in a forth one. To accelerate these operations we will invoke the kernels we designed in the previous part of our tutorial.

Write your application-

Given the above information we can now proceed to write our application code. The source code presented below accomplishes the objectives described in the above section. Even though our API was designed to be simple and intuitive, let's break it up into pieces and analyze them to gain a holistic understanding.

demo_coral.cpp

#include <cstdlib>
#include <iostream>

#include <coral-api/coral.h>

void random(inaccel::vector<float> vector, int size)
{
    for (int i = 0; i < size; i++)
    {
        vector[i] = (float) rand();
    }
}

int main()
{
    int size = 1024 * 1024;

    // Allocate four vectors
    inaccel::vector<float> a(size), b(size), c_add(size), c_sub(size);

    // Initialize input vectors with random values
    random(a, size);
    random(b, size);

    // Send a synchronous (blocking) or an asynchronous (non-blocking) request
    // for "addition" accelerator to the Coral FPGA Resource Manager
    // Request arguments must comply with the accelerator's specific argument list
    inaccel::Request add_req {"com.inaccel.math.vector.addition"};
    add_req.Arg(a).Arg(b).Arg(c_add).Arg(size);
    inaccel::Coral::Submit(add_req);
    //inaccel::Coral::SubmitAsync(add_req);

    // Send a synchronous (blocking) or an asynchronous (non-blocking) request
    // for "subtraction" accelerator to the Coral FPGA Resource Manager
    // Request arguments must comply with the accelerator's specific argument list
    inaccel::Request sub_req {"com.inaccel.math.vector.subtraction"};
    sub_req.Arg(a).Arg(b).Arg(c_sub).Arg(size);
    inaccel::Coral::Submit(sub_req);
    //inaccel::Coral::SubmitAsync(sub_req);

    //inaccel::Coral::Await(add_req);
    //inaccel::Coral::Await(sub_req);

    // Check output vectors
    bool valid = true;
    for (unsigned int i = 0; i < size; i++)
    {
        if (c_add[i] != (a[i] + b[i])) valid = false;

        if (c_sub[i] != (a[i] - b[i])) valid = false;
    }
    if (valid) std::cout << "Results: RIGHT!" << std::endl;
    else std::cout << "Results: WRONG!" << std::endl;

    return 0;
}

What is inaccel::vector?-

It is simply a type alias for std::vector with InAccel's custom allocator. Therefore, both inaccel::vector and std::vector adhere to same usage principles. Whenever you must supply a kernel argument whose type contains a trailing star ('*'), you should use inaccel::vector.

What is inaccel::Request?-

It represents an accelerator request that will be later submitted to Coral for execution. The name of the request (which is passed as constructor argument) should follow the naming convention explained below.

What name should I pass to inaccel::Request?-

The name of your request should be identical to the the corresponding accelerator's function prototype. Be aware that you must have deployed your bitstream for which you wish to invoke its kernels. If no such valid accelerator exists the request will be dismissed.

For the sake of completeness, we present again the portion showing the function prototypes of the available accelerators printed by inaccel bitstream list 9b65d0d622eb command:

ACCELERATORS    com.inaccel.math.vector.addition (float16* input1, float16* input2, float16* output, int size)
                com.inaccel.math.vector.subtraction (float16* input1, float16* input2, float16* output, int size)

Hence, to create requests for those kernels we simply have to invoke the inaccel::Request constructor with argument com.inaccel.math.vector.addition for addition and com.inaccel.math.vector.subtraction for subtraction acceleration respectively.

In case of invoking an accelerator with multiple versions, the latest is the one to be executed by Coral.

What arguments should I pass to inaccel::Request?-

The arguments for the request should match the argument list of the target accelerator as specified in its function prototype. Be aware that the ordering of arguments, as in any function, should be preserved.

As mentioned above, the types which hold a trailing star represent an inaccel::vector of the specified type. The inaccel::Request is populated with arguments through the inaccel::Request::Arg method.

Therefore, based on the prototypes of our accelerators, we should populate the request with arguments of the following types:

  1. inaccel::vector<float>
  2. inaccel::vector<float>
  3. inaccel::vector<float>
  4. int

How to execute an inaccel::Request?-

Through the API call inaccel::Coral::Submit with your request as argument. By invoking that function you transmit your accelerator request to Coral for scheduling and execution.

The inaccel::Coral::Submit executes the accelerator request synchronously (i.e blocks until completion). Otherwise, you can force asynchronous submission by invoking inaccel::Coral::SubmitAsync and ensure completion be using inaccel::Coral::Await.

Full C++ API documentation is available here.

Compilation-

To generate your application's executable you simply have to link against Coral API library.

Below you can see an example of compiling the demo_coral.cpp application:

g++ -std=c++11 -O2 demo_coral.cpp -lcoral-api -o demo_app

C++11 is the minimum supported version.

Run your application-

You run your application exactly the same way as any other application.

Open up a terminal and execute the following command:

./demo_app

Conclusion-

That's it! After this tutorial we hope you got a grasp of how to deploy bitstreams and easily access their kernels from within your accelerated applications by using Coral. Do not forget to follow the links and navigate through our documentation site to explore more advanced use cases.