Commit c824a896 authored by Jens Korinth's avatar Jens Korinth
Browse files

Update examples

* added project to contain all subprojects
* updated all examples to latest version and cleaned dirs
* all examples are building again, not sure if they work
parent 4e2f91de
#!/bin/bash
# #
# Copyright (C) 2014 Jens Korinth, TU Darmstadt # Copyright (C) 2014 Jens Korinth, TU Darmstadt
# #
...@@ -17,8 +16,17 @@ ...@@ -17,8 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Tapasco. If not, see <http://www.gnu.org/licenses/>. # along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
# #
FILENAME=$1 cmake_minimum_required(VERSION 2.6)
CSV=$FILENAME.csv project(examples)
PDF=$FILENAME.pdf set (CMAKE_INSTALL_PREFIX "bin")
include (examples.cmake)
cat job-speed.gnuplot | sed "s/<CSV>/$CSV/g" | sed "s/<PDF>/$PDF/g" | gnuplot add_subdirectory(arrayinit)
add_subdirectory(arraysum)
add_subdirectory(arrayupdate)
add_subdirectory(basic_test)
add_subdirectory(benchmark-alloc-free)
add_subdirectory(benchmark-cd)
add_subdirectory(benchmark-latency)
add_subdirectory(benchmark-mem)
add_subdirectory(memcheck)
# Tapasco Examples # Tapasco Examples
## Building the examples ## Building the examples
This directory contains *TPC API* example programs for each of the trivial This directory contains *TaPaSCo API* example programs for each of the trivial
kernels contained in the initial release of *Tapasco*. Each sub- kernels contained in the initial release of **Tapasco**. Each subdirectory
directory contains a Makefile, you can build all variants automatically using contains a build file for [CMake][1] to generate the build files. You can simply
build the examples out-of-tree by moving to its directory and running
```sh
mkdir build && cd build && cmake .. && make
``` ```
make
If you would like to compile with release settings, use
```sh
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make
``` ```
This should build each example in three variants: This should build each example in two variants:
* <KERNEL>-example * `<KERNEL>-example`
Single-threaded execution of test runs. Single-threaded execution of test runs.
* <KERNEL>-example-mt * `<KERNEL>-example-mt`
Multi-threaded execution of test runs based on Pthreads. Multi-threaded execution of test runs based on Pthreads.
* <KERNEL>-example-mt-ff
Multi-threaded execution of test runs based on FastFlow ff_farm; number of
workers will correspond to value of sysconf(_SC_NPROCESSORS_CONF).
For the FastFlow-variant it is necessary to point the `FF_ROOT` environment to ## Building all examples at once
the installation directory of FastFlow. It was tested against REPARA FastFlow If you'd rather build all examples at once, there is a `CMakeLists.txt` that
v2.0.6 (as delivered in D6.1). gathers all subprojects in a single build. To use it, run
```sh
mkdir -p build && cd build && cmake .. && make
```
Or, for release mode build:
```sh
mkdir -p build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make
```
**NOTE** This does not build `libtapasco` and `libplatform` in debug mode! Use
`tapasco-build-libs` for rebuilding the libraries.
## Composing a suitable hardware threadpool ## Composing a suitable hardware threadpool
With the exception of `memcheck`, which does not require any specific kernel to With the exception of `memcheck`, which does not require any specific kernel to
be instantiated in the composition, all other examples provide a configuration be instantiated in the composition, all examples require a suitable bitstream
file `<KERNEL>.cfg` in their respective directory which can be used to compose for the FPGA with at least one instance of the kernel used in the example.
a hardware threadpool with 48 instances of the kernel: Some time measurements may depend on a *fixed design frequency of 100 MHz*.
For testing purposes, build the bitstreams with that frequency, e.g.,
``` ```sh
cd $TAPASCO_HOME && TAPASCO_MODE=sim TAPASCO_FREQ=250 sbt "compose configFile ..." tapasco compose [arrayupdate x 1] @ 100 MHz --platforms vc709
``` ```
## Running the examples ## Running the examples
The examples can be run against a virtual FPGA provided by simulation, see the The examples can usually be run without inputs, but check the outputs of each
Tapasco documentation for more details. program for errors. Every example will output some information about correctness
Every example will output some information about correctness of each run, and of each run, and will conclude with either `SUCCESS!` or `FAILURE`.
will conclude with either `SUCCESS!` or `FAILURE`. Verbose debug output for the
underlying *TPC API* and *Platform API* implementations can be activated using ## Debugging and Troubleshooting
the `source $TAPASCO_HOME/sim_setup.sh`. Verbose debug output for the underlying *Tapasco API* and *Platform API* implementations can be activated using the *debug mode libraries*, which can be
build via
```sh
tapasco-build-libs --mode debug --rebuild
```
The output can be controlled using two environment variables, `LIBTAPASCO_DEBUG`
and `LIBPLATFORM_DEBUG`. Each controls a 32bit bitfield, where each bit
enables/disables logging of a specific part of the library. By default, logging
is to `stdout` and `stderr`. You can redirect into logfiles by setting
`LIBTAPASCO_LOGFILE` and `LIBPLATFORM_LOGFILE`.
Example for running a program with full logging:
```
LIBTAPASCO_DEBUG=-1 LIBTAPASCO_LOGFILE=libtapasco.log LIBPLATFORM_DEBUG=-1 \
LIBPLATFORM_LOGFILE=libplatform.log <PROGRAM> ...
```
For convenience you can also set the environment variables for the current shell:
```sh
export LIBTAPASCO_DEBUG=-1
export LIBTAPASCO_LOGFILE=libtapasco.log
export LIBPLATFORM_DEBUG=-1
export LIBPLATFORM_LOGFILE=libplatform.log
```
All programs run in the same shell will automatically use these values.
Note: Example programs must currently be run in the same directory as simulation. Please note that the debug libraries *should never be used for performance
measurements*! The logging is carefully designed to minimize the overhead, but
the overhead compared to the release builds is significant, nevertheless.
[1]: https://cmake.org/
cmake_minimum_required(VERSION 2.7) cmake_minimum_required(VERSION 2.6)
project(arrayinit) project(arrayinit)
set (CMAKE_INSTALL_PREFIX "..") set (CMAKE_INSTALL_PREFIX "..")
include (../examples.cmake) include (../examples.cmake)
...@@ -9,5 +9,9 @@ add_executable(arrayinit arrayinit-example.c ../../kernel/arrayinit/arrayinit.c) ...@@ -9,5 +9,9 @@ add_executable(arrayinit arrayinit-example.c ../../kernel/arrayinit/arrayinit.c)
target_link_libraries(arrayinit rt pthread tapasco platform atomic) target_link_libraries(arrayinit rt pthread tapasco platform atomic)
set_target_properties(arrayinit PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label") set_target_properties(arrayinit PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label")
add_executable(arrayinit-mt arrayinit-example-mt.c ../../kernel/arrayinit/arrayinit.c)
target_link_libraries(arrayinit-mt rt pthread tapasco platform atomic)
set_target_properties(arrayinit-mt PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label")
install(TARGETS arrayinit install(TARGETS arrayinit
RUNTIME DESTINATION bin/${ARCH}) RUNTIME DESTINATION bin/${ARCH})
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
//! @file arrayinit-example-mt-ff.cc
//! @brief TPC API based example program exercising a hardware threadpool
//! containing instances of the arrayinit kernel.
//! Multi-threaded FastFlow variant.
//! @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
//!
#include <cerrno>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cassert>
#include <tapasco.h>
#include <ff/farm.hpp>
#include "arrayinit.h"
using namespace ff;
using namespace rpr::tapasco;
#define SZ 256
#define RUNS 25
static tapasco_ctx_t *ctx;
static tapasco_dev_ctx_t *dev;
static int *arr;
int errs = 0;
static void check(int const result)
{
if (! result) {
std::cerr << "fatal error: " << strerror(errno) << std::endl;
exit(errno);
}
}
static void check_tapasco(tapasco_res_t const result)
{
if (result != TAPASCO_SUCCESS) {
std::cerr << "tapasco fatal error: " << tapasco_strerror(result)
<< std::endl;
exit(result);
}
}
static void init_array(int *arr, size_t sz)
{
for (size_t i = 0; i < sz; ++i)
arr[i] = -1;
}
unsigned int check_array(int *arr, size_t sz)
{
unsigned int errs = 0;
for (size_t i = 0; i < sz; ++i) {
if (arr[i] != static_cast<int>(i)) {
std::cerr << "wrong data at " << i << " [" << arr[i]
<< "]" << std::endl;
++errs;
}
}
return errs;
}
static int runTest(int const run)
{
// allocate mem on device and copy array part
tapasco_handle_t h = tapasco_device_alloc(dev, SZ * sizeof(int), 0);
check(h != 0);
// get a job id and set argument to handle
tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 11,
TAPASCO_ACQUIRE_JOB_ID_BLOCKING);
std::cout << "run " << run << ": j_id = " << j_id << std::endl;
check(j_id > 0);
check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h));
// shoot me to the moon!
check_tapasco(tapasco_device_job_launch(dev, j_id, TAPASCO_JOB_LAUNCH_BLOCKING));
// get the result
check_tapasco(tapasco_device_copy_from(dev, h, &arr[SZ * run],
SZ * sizeof(int), TAPASCO_COPY_BLOCKING));
unsigned int errs = check_array(&arr[SZ * run], SZ);
std::cout << std::endl << "RUN " << run << ": " <<
(errs == 0 ? "OK" : "NOT OK") << std::endl;
tapasco_device_free(dev, h);
tapasco_device_release_job_id(dev, j_id);
return errs;
}
struct Emitter: ff_node_t<int> {
int *svc(int *)
{
for (size_t i = 0; i < RUNS; ++i)
ff_send_out(new int(i));
return EOS;
}
};
struct Worker: ff_node_t<int> {
int *svc(int *task)
{
int &t = *task;
t = runTest(t);
return task;
}
};
struct Collector: ff_node_t<int> {
int *svc(int *t)
{
__sync_fetch_and_add(&errs, *t);
delete t;
return GO_ON;
}
void svc_end() { std::cout << "Total number of errors: " << errs << std::endl; }
};
int main(int argc, char **argv)
{
int errs = 0;
// init threadpool
check_tapasco(tapasco_init(&ctx));
check_tapasco(tapasco_create_device(ctx, 0, &dev, 0));
// check arrayinit instance count
std::cout << "instance count: " << tapasco_device_func_instance_count(dev, 11)
<< std::endl;
assert(tapasco_device_func_instance_count(dev, 11));
// init whole array to subsequent numbers
arr = (int *)malloc(SZ * RUNS * sizeof(int));
check(arr != NULL);
init_array(arr, SZ * RUNS);
// setup ff_farm
std::vector<ff_node *> f;
for (int i = 0; i < sysconf(_SC_NPROCESSORS_CONF); ++i)
f.push_back(new Worker);
Emitter e;
Collector c;
ff_farm<> farm(f, &e, &c);
farm.set_scheduling_ondemand();
farm.cleanup_workers();
farm.run_and_wait_end();
if (! errs)
std::cout << "SUCCESS" << std::endl;
else
std::cerr << "FAILURE" << std::endl;
// de-initialize threadpool
tapasco_destroy_device(ctx, dev);
tapasco_deinit(ctx);
free(arr);
return errs;
}
...@@ -79,25 +79,27 @@ static void *runTest(void *p) ...@@ -79,25 +79,27 @@ static void *runTest(void *p)
unsigned int errs = 0; unsigned int errs = 0;
while ((run = __sync_sub_and_fetch(&runs, 1)) > 0) { while ((run = __sync_sub_and_fetch(&runs, 1)) > 0) {
// allocate mem on device and copy array part // allocate mem on device and copy array part
tapasco_handle_t h = tapasco_device_alloc(dev, SZ * sizeof(int), 0); tapasco_handle_t h;
tapasco_device_alloc(dev, &h, SZ * sizeof(int), 0);
check(h != 0); check(h != 0);
// get a job id and set argument to handle // get a job id and set argument to handle
tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 11, tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 11,
TAPASCO_ACQUIRE_JOB_ID_BLOCKING); TAPASCO_DEVICE_ACQUIRE_JOB_ID_BLOCKING);
printf("run %ld: j_id = %d\n", run, j_id); printf("run %ld: j_id = %d\n", run, j_id);
check(j_id > 0); check(j_id > 0);
check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h)); check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h));
// shoot me to the moon! // shoot me to the moon!
check_tapasco(tapasco_device_job_launch(dev, j_id, TAPASCO_JOB_LAUNCH_BLOCKING)); check_tapasco(tapasco_device_job_launch(dev, j_id,
TAPASCO_DEVICE_JOB_LAUNCH_BLOCKING));
// get the result // get the result
check_tapasco(tapasco_device_copy_from(dev, h, &arr[SZ * run], check_tapasco(tapasco_device_copy_from(dev, h, &arr[SZ * run],
SZ * sizeof(int), TAPASCO_COPY_BLOCKING)); SZ * sizeof(int), TAPASCO_DEVICE_COPY_BLOCKING));
errs += check_array(&arr[SZ * run], SZ); errs += check_array(&arr[SZ * run], SZ);
printf("\nRUN %ld %s\n", run, errs == 0 ? "OK" : "NOT OK"); printf("\nRUN %ld %s\n", run, errs == 0 ? "OK" : "NOT OK");
tapasco_device_free(dev, h); tapasco_device_free(dev, h, 0);
tapasco_device_release_job_id(dev, j_id); tapasco_device_release_job_id(dev, j_id);
} }
return errs == 0 ? 0 : (void *)1; return errs == 0 ? 0 : (void *)1;
......
...@@ -10,5 +10,9 @@ add_executable(arraysum arraysum-example.c ../../kernel/arraysum/arraysum.c) ...@@ -10,5 +10,9 @@ add_executable(arraysum arraysum-example.c ../../kernel/arraysum/arraysum.c)
target_link_libraries(arraysum rt pthread tapasco platform atomic) target_link_libraries(arraysum rt pthread tapasco platform atomic)
set_target_properties(arraysum PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label") set_target_properties(arraysum PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label")
add_executable(arraysum-mt arraysum-example-mt.c ../../kernel/arraysum/arraysum.c)
target_link_libraries(arraysum-mt rt pthread tapasco platform atomic)
set_target_properties(arraysum-mt PROPERTIES COMPILE_FLAGS "-g -Wall -Werror -std=gnu99 -Wno-unused-label")
install(TARGETS arraysum install(TARGETS arraysum
RUNTIME DESTINATION bin/${ARCH}) RUNTIME DESTINATION bin/${ARCH})
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
//! @file arraysum-example-mt-ff.cc
//! @brief TPC API based example program exercising a hardware threadpool
//! containing instances of the arraysum kernel.
//! Multi-threaded FastFlow variant.
//! @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
//!
#include <cerrno>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cassert>
#include <tapasco.h>
#include <ff/farm.hpp>
#include "arraysum.h"
using namespace ff;
using namespace rpr::tapasco;
#define SZ 256
#define RUNS 10000
static tapasco_ctx_t *ctx;
static tapasco_dev_ctx_t *dev;
static int *arr;
int errs = 0;
static void check(int const result)
{
if (! result) {
std::cerr << "fatal error: " << strerror(errno) << std::endl;
exit(errno);
}
}
static void check_tapasco(tapasco_res_t const result)
{
if (result != TAPASCO_SUCCESS) {
std::cerr << "tapasco fatal error: " << tapasco_strerror(result)
<< std::endl;
exit(result);
}
}
static void init_array(int *arr, size_t sz)
{
for (size_t i = 0; i < sz; ++i)
arr[i] = i;
}
static int runTest(int const run)
{
int const golden = arraysum(&arr[run * SZ]);
printf("Golden output for run %d: %d\n", run, golden);
// allocate mem on device and copy array part
tapasco_handle_t h = tapasco_device_alloc(dev, SZ * sizeof(int), 0);
check(h != 0);
check_tapasco(tapasco_device_copy_to(dev, &arr[SZ * run], h, SZ * sizeof(int),
TAPASCO_COPY_BLOCKING));
// get a job id and set argument to handle
tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 10,
TAPASCO_ACQUIRE_JOB_ID_BLOCKING);
printf("run %d: j_id = %d\n", run, j_id);
check(j_id > 0);
check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h));
// shoot me to the moon!
check_tapasco(tapasco_device_job_launch(dev, j_id, TAPASCO_JOB_LAUNCH_BLOCKING));
// get the result
int32_t r = 0;
check_tapasco(tapasco_device_job_get_return(dev, j_id, sizeof(r), &r));
printf("FPGA output for run %d: %d\n", run, r);
printf("\nRUN %d %s\n", run, r == golden ? "OK" : "NOT OK");
tapasco_device_release_job_id(dev, j_id);
return r == golden ? 0 : 1;
}
struct Emitter: ff_node_t<int> {
int *svc(int *)
{
for (size_t i = 0; i < RUNS; ++i)
ff_send_out(new int(i));
return EOS;
}
};
struct Worker: ff_node_t<int> {
int *svc(int *task)
{
int &t = *task;
t = runTest(t);
return task;
}
};
struct Collector: ff_node_t<int> {
int *svc(int *t)
{
__sync_fetch_and_add(&errs, *t);
delete t;
return GO_ON;
}
void svc_end() { std::cout << "Total number of errors: " << errs << std::endl; }
};
int main(int argc, char **argv)
{
int errs = 0;
// initialize threadpool
check_tapasco(tapasco_init(&ctx));
check_tapasco(tapasco_create_device(ctx, 0, &dev, 0));
// check arraysum instance count
std::cout << "instance count: " << tapasco_device_func_instance_count(dev, 10)
<< std::endl;
assert(tapasco_device_func_instance_count(dev, 10));
// init whole array to subsequent numbers
arr = (int *)malloc(SZ * RUNS * sizeof(int));
check(arr != NULL);
init_array(arr, SZ * RUNS);
// setup ff_farm
std::vector<ff_node *> f;
for (int i = 0; i < sysconf(_SC_NPROCESSORS_CONF); ++i)
f.push_back(new Worker);
Emitter e;
Collector c;
ff_farm<> farm(f, &e, &c);
farm.set_scheduling_ondemand();
farm.cleanup_workers();
farm.run_and_wait_end();
if (! errs)
std::cout << "SUCCESS" << std::endl;
else
std::cerr << "FAILURE" << std::endl;
// de-initialize threadpool
tapasco_destroy_device(ctx, dev);
tapasco_deinit(ctx);
free(arr);
return errs;
}
...@@ -68,27 +68,29 @@ static void *runTest(void *p) { ...@@ -68,27 +68,29 @@ static void *runTest(void *p) {
int const golden = arraysum(&arr[run * SZ]); int const golden = arraysum(&arr[run * SZ]);
printf("Golden output for run %ld: %d\n", run, golden); printf("Golden output for run %ld: %d\n", run, golden);
// allocate mem on device and copy array part // allocate mem on device and copy array part
tapasco_handle_t h = tapasco_device_alloc(dev, SZ * sizeof(int), 0); tapasco_handle_t h;
tapasco_device_alloc(dev, &h, SZ * sizeof(int), 0);
check(h != 0); check(h != 0);
check_tapasco(tapasco_device_copy_to(dev, &arr[SZ * run], h, check_tapasco(tapasco_device_copy_to(dev, &arr[SZ * run], h,
SZ * sizeof(int), TAPASCO_COPY_BLOCKING)); SZ * sizeof(int), TAPASCO_DEVICE_COPY_BLOCKING));
// get a job id and set argument to handle // get a job id and set argument to handle
tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 10, tapasco_job_id_t j_id = tapasco_device_acquire_job_id(dev, 10,
TAPASCO_ACQUIRE_JOB_ID_BLOCKING); TAPASCO_DEVICE_ACQUIRE_JOB_ID_BLOCKING);
printf("run %ld: j_id = %d\n", run, j_id); printf("run %ld: j_id = %d\n", run, j_id);
check(j_id > 0); check(j_id > 0);
check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h)); check_tapasco(tapasco_device_job_set_arg(dev, j_id, 0, sizeof(h), &h));
// shoot me to the moon! // shoot me to the moon!
check_tapasco(tapasco_device_job_launch(dev, j_id, TAPASCO_JOB_LAUNCH_BLOCKING)); check_tapasco(tapasco_device_job_launch(dev, j_id,