10. C++ Developement¶
If you are interested in creating additional functionality for Bifrost, this document should help you get up and running with the Bifrost C++ API.
10.1. Create a Ring Buffer and Load Data¶
#include <cuda_runtime_api.h>
#include <bifrost/common.h>
#include <bifrost/ring.h>
...
//////////////////////////////////
//Create and write to a ring
/////////////////////////////////
BFring my_ring; //declare our ring variable
bfRingCreate(&my_ring, BF_SPACE_SYSTEM); //initiate this ring on local memory (=BF_SPACE_SYSTEM)
bfRingBeginWriting(my_ring); //begin writing to this ring
//generate some dummy variables
BFoffset skip_time_tag = -1;
BFoffset skip_offset = 0;
BFsize my_header_size = 0;
char *my_header = "";
//Set our ring variables
BFsize nringlets = 1; //dimensionality of our ring.
BFsize nbytes = 32*8; //number of bytes we are putting in
BFsize buffer_bytes = 4*nbytes;
//resize ring to fit the data
bfRingResize(
my_ring, nbytes, buffer_bytes, nringlets);
//open a sequence on the ring.
BFwsequence my_sequence;
const char* name = "mysequence"; //we can find our sequence by this name later on
bfRingSequenceBegin(
&my_sequence, my_ring, name, skip_time_tag,
my_header_size, (const void*)my_header, nringlets,
skip_offset);
//reserve a "span" on this sequence to put our data
BFwspan my_span;
bfRingSpanReserve(
&my_span, my_ring, nbytes);
void *data_access; //create a pointer to pass our data to
//create the data and copy it to the ring
float data[8] = {10, -10, 0, 0, 1, -3, 1, 0};
bfRingSpanGetData((BFspan)my_span, &data_access); //point our pointer to the span's allocated memory
memcpy(data_access, &data, nbytes);
//stop writing
bfRingSpanCommit(my_span, nbytes); //commit this span to memory
bfRingSequenceEnd(my_sequence, skip_offset); //end off the sequence
bfRingEndWriting(my_ring);
//////////////////////////////////
//Read from the ring
/////////////////////////////////
nbytes = 32*8; //the size of the data we want to read
//open the last accessed sequence
BFrsequence my_read_sequence;
bfRingSequenceOpenLatest(
&my_read_sequence, my_ring, true);
//open a span on this sequence
BFrspan my_read_span;
bfRingSpanAcquire(
&my_read_span, my_read_sequence, skip_offset, nbytes);
//Access the data from the span with a pointer
bfRingSpanGetData((BFspan)my_read_span, &data_access);
float *my_data = static_cast<float*>(data_access); //Copy the data into a readable format
//print out the ring data
for (int i = 0; i < 8; i++)
{
printf("%f\n",my_data[i]);
}
//close up our ring access
bfRingSpanRelease(my_read_span);
bfRingSequenceClose(my_read_sequence);
bfRingDestroy(my_ring); //delete the ring from memory
10.2. Adding New Packet Formats¶
A wide variety of packet formats are already included in Bifrost. For simplicity, it is likely preferable to make use of these pre-existing formats. In the case that this becomes infeasible, here are some of what is necessary in order to add a new format to Bifrost.
Files to edit:
python/bifrost/packet_capture.pyAdd
set_mypacketto thePacketCaptureCallbackclass. It will likely look very similar to theset_chipsmethod.
src/bifrost/packet_capture.hThis is for ctypesgen. Add a typedef for the sequence callback. This typedef corresponds to the sequence callback used in the packet reader, see the sections on
test_udp_io.pyandtest_disk_io.pyfor examples of writing the packet reader.Also declare the capture callback.
src/formats/format.hppAdd a one-line
#include "mypacket.hpp"
src/formats/mypacket.hpp
This is the only file that will need to be fully written from scratch. The easiest way to proceed is to copy the most similar existing packet format and modify it accordingly. One will need to make sure that the header is defined properly and that the correct information is going into it, and one will need to make sure that the memcpy operation is properly filling the packet with data.
src/packet_capture.cppNeed to add a call to the packet capture callback.
src/packet_capture.hppThis is where you will spend most of your time. Add your packet capture sequence callback to the
BFpacketcapture_callback_implinitialization list. Immediately after the initialization list, add theset_mypacketandget_mypacketmethods.Add a new class:
BFpacketcapture_mypacket_impl. In the case of simpler packet formats, this may be very close to the already writtenBFpacketcapture_chips_impl. It’s probably best to start by copying the format that is closest to the format you are writing and modify it.In
BFpacketcapture_create, add the format to the first long if-else if statement. This section tells the disk writer the size of the packet to expect. Then add your packet to the second if-else if statement.
src/packet_writer.hppAfter the
BFpacketwriter_generic_impl, add a classBFpacketwriter_mypacket_impl. Take care to choose the correct BF_DTYPE_???.In
BFpacketwriter_create, add your packet to the first if-else if statement. Note that nsamples needs to correspond to the number elements in the data portion of the packet. Then add your packet to the third if-else if statement along all the other formats.
test/test_disk_io.pyAdd a reader for your packet format. This reader will be what is used in the actual code as well. It contains the sequence callback that we declared in the
src/bifrost/packet_capture.hfile. Note that the header in this sequence callback is the ring header not the packet header.You will also need to add a
_get_mypacket_data,test_write_mypacket, andtest_read_mypacket.
test/test_udp_io.pyThe UDP version of
test_disk_io. Once you have written the disk I/O test, this test is fairly simple to implement, provided you wrote it correctly.