Question

I try to make a system, where I need a activator function. This activator function may have a state, so I tried to put the state into an concurrency::array_view. When I try to build the solution, I get the following linker error.

Error 2 error LNK2019: unresolved external symbol "public: static double __thiscall ArtNeuroNet::ActivationFunctions::Activator::function(double,class Concurrency::array_view) restrict(cpu, amp)" (?function@Activator@ActivationFunctions@ArtNeuroNet@@SENNV?$array_view@N$00@Concurrency@@@DZ_B) referenced in function _wmain D:\Projekte\ArtNeuroNet\ArtNeuroNet\ArtNeuroNet.obj ArtNeuroNet

Error 3 error LNK1120: 1 unresolved externals D:\Projekte\ArtNeuroNet\Debug\ArtNeuroNet.exe 1 1 ArtNeuroNet

My simplified activator looks like

double Activator::lin_function(double x, concurrency::array_view<double, 1> state)
    restrict(cpu, amp)
{
    if (x > state[StateParameterType::ThresholdParameter])
        return 1;
    else if (x < -(state[StateParameterType::ThresholdParameter]))
        return 0;
    else
        return state[StateParameterType::AlphaParameter] * x + 0.5f;
}

double* Activator::getInitialState(double alpha)
{
    double stateCpu[] = {1.0, 0.5};

    if (alpha != NULL)
        stateCpu[0] = alpha;

    return stateCpu;
}

My activator creation looks like

Activator activator = *(new Activator());
double* stateCpu = activator.getInitialState(1.0);

concurrency::array_view<double, 1> state(2, stateCpu);

activator.lin_function(0.4, state);

For testing purpose, I added the last line, which is the call activator.lin_function(0.4, state). If I comment that line out, the project gets build without any problems.

My question is now, what am I missing out or am I doing, which I'm acctually not supposed to do?

EDIT

The method lin_function is private. However, in the Activator there's a public method function which calls the private method lin_function. For simplification-reasons, I left that part of the source coude out. Both methods are accessable, as I otherwise wouldn't be able to build the solution, when the call to the method lin_function is commented out.

Activator resides completely within the same project, which is a console-application for now.

The lin_function relies on a single state variable. However, a threshold-implementation of the activator function doesn't need any state storage at all. Changing everything to the threshold-activation function and commenting the rest out reacts the same way. In particular that is „lin_function commented out -> no linker error, otherwise –> linker error“

EDIT 2

For the Activator there exists a header (Activator.h) and source-file (Activator.cpp). It somehow seems that the linker can't find any method from the header which is declared as restrict(cpu, amp).

// Doesn't get found and throws linker error
double Activator::function(double x)
    restrict(cpu, amp)
{
    return (x >= 0) ? 1 : 0;
}

// Get's found and no linker errors occur
double Activator::function(double x)
{
    return (x >= 0) ? 1 : 0;
}

EDIT 3

After defining all methods containing a restrict(amp, cpu) as static in the header-file, everything did build correctly and ran without problems.

Is there actually a restriction on methods needed to be static when restrict(amp, cpu) is used?

Was it helpful?

Solution

Here are the limitations on C++ AMP with respect to classes and functions etc. You are also limited to a subset of supported C++ types but I don't think that is the issue here.

References and pointers (to a compatible type) may be used locally but cannot be captured by a lambda. Function pointers, pointer-to-pointer, and the like are not allowed; neither are static or global variables.

Classes must meet more rules if you wish to use instances of them. They must have no virtual functions or virtual inheritance. Constructors, destructors, and other nonvirtual functions are allowed. The member variables must all be of compatible types, which could of course include instances of other classes as long as those classes meet the same rules. The actual code in your amp-compatible function is not running on a CPU and therefore can’t do certain things that you might be used to doing:

  • recursion
  • pointer casting
  • use of virtual functions
  • new or delete
  • RTTI or dynamic casting
  • goto
  • throw, try, or catch
  • access to globals or statics
  • inline assembler

This is taken from my book and I apologize because it is misleading. What isn't made clear is that this applies to classes that you wish to pass to AMP kernels as data. Not to classes that have restrict(amp) methods on them. This is only supported for static methods because it is not possible to share the class this pointer with the GPU as it refers to an object instance on the CPU.

So the following is an example of a class that meets the above requirements and can be passed to an AMP kernel:

class stuff
{
public:
    int a;

    stuff(int v) : a(v) { }
};

stuff also meets the requirements for supported types as int is supported by AMP.

The following class uses stuff in an array_view:

class test_case
{
public:
    test_case()
    {
    }

    static int amp_method(int a) restrict(amp, cpu)
    {
        return a * a;
    };

    void test_amp()
    {
        concurrency::array_view<stuff, 1> data(100);
        concurrency::parallel_for_each(data.extent, 
            [data](concurrency::index<1> idx) restrict(amp)
        {
            data[idx].a = amp_method(data[idx].a);
        });
        data.synchronize();
    };

    void test_cpu()
    {
        std::vector<int> data(100, 0);
        for (auto& d : data)
        {
            d = amp_method(d);
        }
    }
};

If you remove the static modifier on amp_method then you get the following error on VS 2013.

warning C4573: the usage of 'test_tools_tests::test_case::amp_method' requires the compiler to capture this but the current default capture mode does not allow it

You may see something different in 2012. One of the weaknesses of the first AMP release was it's errors. This has improved in 2013.

On reflection this all seems really reasonable. How could I pass this to the GPU when it refers to code running on the CPU?

Note that restrict cannot be applied to classes.

Thanks for highlighting this issue. I guess I should update the book errata.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top