Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update C++ API documentation #184

Open
telegraphic opened this issue Oct 17, 2022 · 2 comments
Open

Update C++ API documentation #184

telegraphic opened this issue Oct 17, 2022 · 2 comments

Comments

@telegraphic
Copy link
Collaborator

Hi all,

I've been exploring the C++ API a bit recently, and have found it a bit hard going as there's not much documentation (and my C++ needs more time in the dojo).

There is a great little example here already though, on how to use the ring interface:
http://ledatelescope.github.io/bifrost/Cpp-Development.html

So I thought we could add a few more examples?

Here's an example code that shows how to use bifrost arrays in C++. The code compiles and does what it's supposed to do, but is simple (on purpose, but if there's 'better' ways lemme know).

I have some questions in the comments, would be good if someone could help fill these in!

(PS: It would also be good if we had these in an examples directory/repo, that the user can compile)

#include <bifrost/array.h>
#include <bifrost/common.h>
#include <bifrost/ring.h>
#include <utils.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>

// Simple 'kernel' to add two arrays together, into a third array
BFstatus AddStuff(BFarray *xdata, BFarray *ydata, BFarray *sumdata)
{
    long nelements = num_contiguous_elements(xdata);

    float* x = (float *)xdata->data;
    float* y = (float *)ydata->data;
    float* summed = (float *)sumdata->data;

    for(int i=0; i < nelements; i +=1)
    {
       summed[i] = x[i] + y[i];
    }

    return BF_STATUS_SUCCESS;
}

int main() {
    
    // Create input and output arrays
    BFarray bf_data_x;
    BFarray bf_data_y;
    BFarray bf_data_sum;

    int N = 16;  // 16 samples in array

    // dtype codes are set in array.h
    // https://github.com/ledatelescope/bifrost/blob/master/src/bifrost/array.h#L42
    // space codes are set in memory.h
    // https://github.com/ledatelescope/bifrost/blob/a57a83f8bffc91feed5146d7264a72e0c8ddeb6d/src/bifrost/memory.h#L43
    bf_data_x.dtype = BF_DTYPE_I32;
    bf_data_x.space = BF_SPACE_SYSTEM;
    bf_data_x.ndim = 1;
    bf_data_x.shape[0] = N;

    //BFstatus codes are set in https://github.com/ledatelescope/bifrost/blob/master/src/bifrost/common.h#L54
    //BF_CHECK checks codes, the error codes are found in assert.hpp
    //https://github.com/ledatelescope/bifrost/blob/master/src/assert.hpp#L146

    BF_CHECK(bfArrayMalloc(&bf_data_x));

    // Fill with test vector 
    int* data_x = (int *)bf_data_x.data;
    for(int i=0; i < N; i++) {
        data_x[i] = i;
    }

    // Repeat for Y data arrray
    bf_data_y.dtype = BF_DTYPE_I32;
    bf_data_y.space = BF_SPACE_SYSTEM;
    bf_data_y.ndim = 1;
    bf_data_y.shape[0] = N;
    BF_CHECK(bfArrayMalloc(&bf_data_y));

    // Fill with test vector - set all to 1
    int* data_y = (int *)bf_data_y.data;
    for(int i=0; i < N; i++) {
        data_y[i] = 1;
    }

    // Repeat for Y data arrray
    bf_data_sum.dtype = BF_DTYPE_I32;
    bf_data_sum.space = BF_SPACE_SYSTEM;
    bf_data_sum.ndim = 1;
    bf_data_sum.shape[0] = N;
    BF_CHECK(bfArrayMalloc(&bf_data_sum));
    int* data_sum = (int *)bf_data_sum.data;

    // run the AddStuff function
    BF_CHECK(AddStuff(&bf_data_x, &bf_data_y, &bf_data_sum));


    // Print some stuff to screen
     for(int i=0; i < N; i++) {
        cout << "idx[" << i << "]: " << data_x[i] << " + " << data_y[i] << " = " << data_sum[i] << endl;
        // Is this the right way to use BF_ASSERT
        BF_ASSERT( data_x[i] + data_y[i] == data_sum[i], BF_STATUS_INTERNAL_ERROR);
    }   

    // Free memory
    BF_CHECK(bfArrayFree(&bf_data_x));
    BF_CHECK(bfArrayFree(&bf_data_y));
    BF_CHECK(bfArrayFree(&bf_data_sum));

    // TODO: what does bfArrayMemset do? (Show in example)
    // TODO: Show bfArrayCopy in another example 
    // TODO: show dtype2ctype_string from array_util
    
    // TODO: What's the deal with check() in Common.hpp?
    // https://github.com/ledatelescope/bifrost/blob/a57a83f8bffc91feed5146d7264a72e0c8ddeb6d/src/bifrost/Common.hpp#L44
    
    // TODO: What's the deal with _check in the python wrappers?
    // Should I be using this in my Python plugins?

    // Note: Am I using BF_CHECK correctly? 
    // Note: looks like BF_ASSERT is more common than BF_CHECK? What's the difference?
    // https://github.com/ledatelescope/bifrost/blob/master/src/assert.hpp#L102
    // TODO: Another tutorial on error handling

    // TODO: example with CUDA in it
    // TODO: What's the deal with ArrayIndexer.cuh and ShapeIndexer.cuh? 
}
@jaycedowell
Copy link
Collaborator

I think having more examples, particularly on the C++ side, is a great idea. I'll see if I can find some time to dig into you questions.

@jaycedowell
Copy link
Collaborator

// TODO: What's the deal with check() in Common.hpp?

I'm not sure. It looks to be similar to the BF_CHECK macro except that it raises an exception on an error rather than just printing a message and passing the return value along.

// TODO: What's the deal with _check in the python wrappers?
// Should I be using this in my Python plugins?

_check in Python either catches some particular kinds of non-BF_STATUS_SUCCESS return values and converts them to Python exceptions. The exact behavior is determined by whether or not the Python constant __debug__ is True or False.

// Note: Am I using BF_CHECK correctly?

Looks like it. As an alternative you could also use the more C-like:

if( !bfArrayMalloc(&bf_data_y) ) {
  printf("bfArrayMalloc failed\n");
}

since BF_STATUS_SUCCESS is zero.

// Note: looks like BF_ASSERT is more common than BF_CHECK? What's the difference?

BF_ASSERT throws exceptions in from inside a class method. I think of it as a part of the input validate step in a method, similar to what you would do in Python:

def my_op(input):
  assert(input.shape == 2)
  input[:,0] += 1
  return input

vs.

void my_op(BFarray* input) {
  BF_ASSERT(input->ndim == 2, BF_STATUS_INVALID_SHAPE);
  for(int i=0; i<input->shape[0]; i++) {
    input->data[i*input->shape[1]] += 1;
 }
}

(I hope that's the correct syntax there).

BF_CHECK is meant more for check the return code of a method. My Python example here would be it's like checking the return code on scipy.optimize.leastsq.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants