Syniurge / Calypso

LDC fork to experiment direct interfacing with C++
http://wiki.dlang.org/LDC
Other
135 stars 10 forks source link

FastFlow test case #12

Open wilsonk opened 9 years ago

wilsonk commented 9 years ago

Hello Elie,

There is an issue with trying to instantiate an abstract class from the FastFlow C++ library in a D test program. If I use this simple test program:

modmap (C++) "ff/farm.hpp"; import (C++) ff.ff_node; class Worker : ff_node { //alias svc = cpp.ff.ff_node.ff_node.svc; public: override void* svc(void * task) { return task; } } void main() {}

I get this error:

test.d(10): Error: function test.Worker.svc does not override any function, did you mean to override 'cpp.ff.ff_node.ff_node.svc'?

You can see I tried to use an alias to get the override to work (and I tried a few other things to no avail). I tried making a cpp file and overriding in a simple class and then using that class also, bit it didn't work either.

Thanks, Kelly

Syniurge commented 9 years ago

Hey Kelly,

The issue is with the types' deco:

To preserve C++'s logical const the deco of pointer types are altered, and I didn't realize that this would break function overriding. I'm working on a fix right now (which is going to be ugly but I can't see good alternatives for now).

wilsonk commented 9 years ago

Hello Elie,

Ok, right…I can see the writeByte(‘~’) in ::TypePointer. Too bad there only seems to be an ugly solution, but at least you can see one :)

I think this might be the same issue holding back threads in Dlib also. I will keep posting new issues, as I have run into a couple different ones for other libs also.

Thanks,

Kelly

From: Elie Morisse [mailto:notifications@github.com] Sent: Friday, April 24, 2015 9:27 AM To: Syniurge/Calypso Cc: wilsonk Subject: Re: [Calypso] FastFlow test case (#12)

Hey Kelly,

The issue is with the types' deco:

To preserve C++'s logical const the deco of pointer types are altered, and I didn't realize that this would break function overriding. I'm working on a fix right now (which is going to be ugly but I can't see good alternatives for now).

— Reply to this email directly or view it on GitHub https://github.com/Syniurge/Calypso/issues/12#issuecomment-95966169 .

Syniurge commented 9 years ago

Does beb67eafccbc896500f4cbedb317097e4d326c5b fix it?

Your test works if I use the following as C++ header:

class ff_node
{
public:
    virtual void* svc(void* task) = 0;
};
wilsonk commented 9 years ago

Hello Elie,

Yes, that seems to have gotten us past that particular error. Unfortunately I am seeing a new one with compiling the simple FastFlow example. I am getting this error now when just compiling the simplest ‘Worker : ff_node’ class:

ldc2: /home/wilsonk/Downloads/llvm-3.6/llvm/lib/IR/Constants.cpp:969: llvm::ConstantStruct::ConstantStruct(llvm::StructType, llvm::ArrayRef<llvm::Constant>): Assertion `(T->isOpaque() || V[i]->getType() == T->getElementType(i)) && "Initializer for struct element doesn't match struct element type!"' failed.

Ugh. That is during codegen so the llvm output looks like this (sorry for the spam but hopefully you can see something I am missing…the class type is at the bottom to more easily compare):

declare %test.Worker* @_D4test6Worker6__ctorMFZC4test6Worker(%test.Worker*)

Thanks,

Kelly

wilsonk commented 9 years ago

The fixes pushed today have also fixed the problem above. I am now just seeing an issue with some functions and enums being undefined from ff/utils.hpp. They are in the ff namespace, and I am importing ff._, so I am not sure what is going on there. But I'll keep looking into it.

Syniurge commented 9 years ago

I am now just seeing an issue with some functions and enums being undefined from ff/utils.hpp. They are in the ff namespace, and I am importing ff._, so I am not sure what is going on there. But I'll keep looking into it.

Was it fixed since?

wilsonk commented 9 years ago

Ugh, actually we are right back to square one on this issue. The first test case above gives the exact same error again:

test.d(10): Error: function test.Worker.svc does not override any function, did you mean to override 'cpp.ff.ff_node.ff_node.svc'?

Strange and unfortunate. :(

Syniurge commented 9 years ago

Strange.. my smaller test case works:

// abstract.hpp
class ff_node
{
public:
    virtual void* svc(void* task) = 0;
};
// abstract.d
modmap (C++) "abstract.hpp";
import (C++) ff_node;

class Worker : ff_node {
public:
    override void* svc(void * task) { return task; }
}
wilsonk commented 9 years ago

Woops, sorry Elie. It looks like this is the actual test case that fails:

namespace test {
    class ff_node
    {
    public:
    virtual void* svc(void* task) = 0;
    };
    void* svc(void* task) {
    return 0;
    }
}

modmap (C++) "abstract.hpp";
import (C++) test._;
import (C++) test.ff_node;

class Worker : ff_node {
public:
    override void* svc(void * task) { return task; }
}

And I get this error still:

showcase.d(7): Error: function showcase.Worker.svc does not override any function, did you mean to override 'cpp.test.ff_node.ff_node.svc'?

Syniurge commented 9 years ago

Thanks for cornering the problem Kelly, there was a big oversight and the "equivalent deco" wasn't set for function types past the first one of a kind (I didn't know that they weren't merged during semantic() like the other types).

It was broken again when I simplified the method of checking "type equivalence" introduced by beb67eafccbc896500f4cbedb317097e4d326c5b by introducing these "equivalent deco".

Should get past those errors now.

wilsonk commented 9 years ago

Hi Elie, it looks like things were fixed by that last commit for the problem stated above. Now, however, I am running into quite a few functions that are 'static inlined' in .hpp files being undefined like this:

test.o: In function ff::ff_node::ffTime()': test.d:(.text._ZN2ff7ff_node6ffTimeEv[_ZN2ff7ff_node6ffTimeEv]+0x2c): undefined reference toff::diffmsec(timeval const&, timeval const&)' test.o: In function ff::ff_node::wffTime()': test.d:(.text._ZN2ff7ff_node7wffTimeEv[_ZN2ff7ff_node7wffTimeEv]+0x2c): undefined reference toff::diffmsec(timeval const&, timeval const&)' test.o: In function ff::ff_node::setAffinity(int)': test.d:(.text._ZN2ff7ff_node11setAffinityEi[_ZN2ff7ff_node11setAffinityEi]+0x42): undefined reference toff::error(char const*, ...)' test.o: In function `ff::ff_node::losetime_out()':

etc, etc...

Here is relevant part of the utils.hpp file:

namespace ff {
  static inline double diffmsec(const struct timeval & a,
                              const struct timeval & b) {
    long sec  = (a.tv_sec  - b.tv_sec);
    long usec = (a.tv_usec - b.tv_usec);

    if(usec < 0) {
        --sec;
        usec += 1000000;
    }
    return ((double)(sec*1000)+ ((double)usec)/1000.0);
  }
.
.
.
}

I am modmap'ing and importing everything in utils.hpp, but diffmsec still seems to be undefined no matter what I try. I am tired though...maybe I am just missing something simple??

Thanks, Kelly

Syniurge commented 9 years ago

Hi Kelly,

Sorry for the late reply not a lot of free time atm, those linking errors are the last errors that prevent my Ogre demo from compiling too. I'm investigating them, but the current way of referencing and emitting functions is more like a hack for the time being which is why it's fragile.

The right way to do it would be the DMD way, i.e to traverse the function bodies and to map the function calls to DMD's CallExp which Calypso doesn't do atm, then even if the CallExp aren't used that will make DMD reference, instantiate and emit the C++ functions like it does for D functions. But that will take more time to implement, so I leave that for later.

wilsonk commented 9 years ago

Unfortunately, the recent work that you mention in #25 didn't fix the issues with my fastflow example. I am still seeing the undefined reference to ff:diffmsec, etc...

Just to let you know Elie.

wilsonk commented 9 years ago

Looks like the last few changes today have fixed the ff_msec problems. There are still a few other ones like this:

__cpp_ff_ff_node.o: In function `ff::ff_node::getstarttime() const':
__cpp/ff/ff_node:(.text._ZNK2ff7ff_node12getstarttimeEv[_ZNK2ff7ff_node12getstarttimeEv]+0x20): undefined reference to `timeval::timeval(timeval const&)'
__cpp_ff_ff_node.o: In function `ff::ff_node::getstoptime() const':
__cpp/ff/ff_node:(.text._ZNK2ff7ff_node11getstoptimeEv[_ZNK2ff7ff_node11getstoptimeEv]+0x20): undefined reference to `timeval::timeval(timeval const&)'
__cpp_ff_ff_node.o: In function `ff::ff_node::getwstartime() const':
__cpp/ff/ff_node:(.text._ZNK2ff7ff_node12getwstartimeEv[_ZNK2ff7ff_node12getwstartimeEv]+0x20): undefined reference to `timeval::timeval(timeval const&)'
__cpp_ff_ff_node.o: In function `ff::ff_node::getwstoptime() const':
__cpp/ff/ff_node:(.text._ZNK2ff7ff_node12getwstoptimeEv[_ZNK2ff7ff_node12getwstoptimeEv]+0x23): undefined reference to `timeval::timeval(timeval const&)'
__cpp_ff_ff_node.o: In function `ff::ff_node::thWorker::svc(void*)':
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x10e): undefined reference to `EOS'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x11d): undefined reference to `EOS_NOFREEZE'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x182): undefined reference to `GO_OUT'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x1bd): undefined reference to `GO_OUT'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x1dc): undefined reference to `EOS'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x1eb): undefined reference to `EOS_NOFREEZE'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x200): undefined reference to `EOS'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x21e): undefined reference to `GO_ON'
__cpp/ff/ff_node:(.text._ZN2ff7ff_node8thWorker3svcEPv[_ZN2ff7ff_node8thWorker3svcEPv]+0x22d): undefined reference to `EOS_NOFREEZE'
__cpp_ff_ff_thread.o: In function `ff::ff_thread::spawn(int)':
__cpp/ff/ff_thread:(.text._ZN2ff9ff_thread5spawnEi[_ZN2ff9ff_thread5spawnEi]+0xbb): undefined reference to `ff::proxy_thread_routine(void*)'
__cpp_ff_dynqueue.o: In function `ff::SWSR_Ptr_Buffer::~SWSR_Ptr_Buffer()':
__cpp/ff/dynqueue:(.text._ZN2ff15SWSR_Ptr_BufferD2Ev[_ZN2ff15SWSR_Ptr_BufferD2Ev]+0x11): undefined reference to `freeAlignedMemory(void*)'
collect2: error: ld returned 1 exit status

But it is pretty close now hopefully :)

Syniurge commented 9 years ago

When I found the root cause earlier of most linking errors this week it was more like the beginning of the end of linking errors, because now the burden of referencing and emitting every used symbol falls on Calypso so there's still some work ahead, but yes pretty close we should be! :)

The first four errors (copy constructors) will go away tonight.

wilsonk commented 9 years ago

Sweet, sounds good Elie :)

wilsonk commented 9 years ago

A few more of those errors above were fixed with those commits tonight. The errors from the one ending with 'EOS' down are still there, but this example is almost compiling now. :)

Syniurge commented 9 years ago

How does it look now?

wilsonk commented 9 years ago

Great Elie. Just a couple left:

__cpp_ff_ff_thread.o: In function `ff::ff_thread::spawn(int)':
__cpp/ff/ff_thread:(.text._ZN2ff9ff_thread5spawnEi[_ZN2ff9ff_thread5spawnEi]+0xbb): undefined reference to `ff::proxy_thread_routine(void*)'
__cpp_ff_dynqueue.o: In function `ff::SWSR_Ptr_Buffer::~SWSR_Ptr_Buffer()':
__cpp/ff/dynqueue:(.text._ZN2ff15SWSR_Ptr_BufferD2Ev[_ZN2ff15SWSR_Ptr_BufferD2Ev]+0x11): undefined reference to `freeAlignedMemory(void*)'
collect2: error: ld returned 1 exit status
Syniurge commented 9 years ago

And now?

wilsonk commented 9 years ago

Oh so close...just that last error is still showing up. I can get things to compile if I remove the 'static' keyword for the function freeAlignedMemory, however!!

I will test things further now because at least I can massage things into place for the simplest example.

wilsonk commented 9 years ago

Hey Elie,

The minimal example near the top of this issue compiles and runs with a blank main() or a just an instantiation of Worker() (after I fix freeAlignedMemory as above), which is cool. The next thing I tried was to convert the 'simplest.cpp' example like this:

modmap (C++) "ff/pipeline.hpp";
modmap (C++) "ff/node.hpp";

import (C++) ff._;
import (C++) ff.ff_pipeline;
import (C++) ff.ff_node;

import std.stdio;

class Stage : ff_node {
   public:
        this() { counter = 0; }
        override int svc_init() {
            writeln("Hello, I'm Stage");
            return 0;
        }
        override void* svc(void * task) {
                 if (++counter > 10) return cast(void*)0;
                 writeln("Hi");
                 return cast(void*)ulong.max-0x02;
        }
        override void svc_end() {
                 writeln("Goodbye...");
        }
   private:
        long counter;
}

int main(char[][] args) {

    auto pipe = new ff_pipeline();
    pipe.add_stage(new Stage());
    return 0;
}

It compiles fine but when I try to run this (and a couple other small examples) I get a segfault and this backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b4aaaa in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0  0x00007ffff7b4aaaa in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x0000000000405fde in std::_Rb_tree_iterator<ff::fftree*>::operator--() ()
#2  0x0000000000405e10 in std::_Rb_tree<ff::fftree*, ff::fftree*, std::_Identity<ff::fftree*>, std::less<ff::fftree*>, std::allocator<ff::fftree*> >::_M_get_insert_unique_pos(ff::fftree* const&) ()
#3  0x0000000000405c51 in std::_Rb_tree<ff::fftree*, ff::fftree*, std::_Identity<ff::fftree*>, std::less<ff::fftree*>, std::allocator<ff::fftree*> >::_M_insert_unique(ff::fftree* const&) ()
#4  0x0000000000405b78 in std::set<ff::fftree*, std::less<ff::fftree*>, std::allocator<ff::fftree*> >::insert(ff::fftree* const&) ()
#5  0x0000000000420057 in ff::fftree::fftree(ff::ff_node*, ff::fftype) ()
#6  0x000000000041f125 in ff::fftree::fftree(ff::ff_node*, ff::fftype) ()
#7  0x0000000000425c17 in ff::ff_pipeline::ff_pipeline(bool, int, int, bool) ()
#8  0x000000000042493e in ff::ff_pipeline::ff_pipeline(bool, int, int, bool) ()
#9  0x00000000004054e1 in D main (args=...) at test.d:31
#10 0x0000000000448ea4 in rt.dmain2._d_run_main() (mainFunc=0x405450 <D main>) at dmain2.d:400
#11 0x0000000000448d69 in rt.dmain2._d_run_main() (dg={void delegate() (void)} 0x7fffffffe278) at dmain2.d:375
#12 0x0000000000448e07 in rt.dmain2._d_run_main() () at dmain2.d:400
#13 0x0000000000448d69 in rt.dmain2._d_run_main() (dg={void delegate() (void)} 0x7fffffffe2e8) at dmain2.d:375
#14 0x0000000000448ccf in _d_run_main (argc=1, argv=0x7fffffffe598, mainFunc=0x405450 <D main>) at dmain2.d:408
#15 0x00000000004056c8 in main (argc=1, argv=0x7fffffffe598) at .:3

With each example the problem appears to start at the fftree calls and then the std::set. I suppose something isn't being instantiated properly from the within the pipeline.hpp file...maybe this:

"fftree_ptr = new fftree(this, PIPE);"

when the new ff_pipeline() call is made? Just my thoughts on it anyways.

Syniurge commented 9 years ago

Looking into it.

Syniurge commented 9 years ago

The segfault is gone with 9ffb94ec19fdf68de59571e7a104b1c034b15ef2.

EDIT: oops nevermind, not your segfault, but another one I got during codegen.

Syniurge commented 9 years ago

The last linking error is fixed with e56876f1627bb12a1fa108b049126c27c6a077dc, and it runs without segfaulting.

Syniurge commented 9 years ago

Your segfault seems to be something else though, maybe a wrong default argument passed to ff_pipeline's ctor. Since I can't reproduce the crash, could you examine the arguments in the 8th stack frame?

wilsonk commented 9 years ago

Elie, it looks like the 'this' at line 154 of pipeline.hpp is the problem. I can just instantiate a 'new ff_pipeline()' manually in my test file and then recreate line 154 like this:

    auto pipe = new ff_pipeline();
    pipe.fftree_ptr = new fftree(pipe, fftype.PIPE);

this gets me past the pipeline constructor, but then things segfault in fftree.hpp at line 69 'roots.insert(this);' ... so it looks like the 'this' is the problem here also. If I comment out line 68-69 in fftree.hpp then the pipeline test above runs fine.

Anyways, the reason I am doing this manually is because I can't get gdb to find the line numbers/symbols for pipeline.hpp to save my life!!! I am compiling with -g for clang and ldc2 (or -ggdb or -gc, etc.) and I can use readelf or nm to see that there are debug symbols in my 'test' binary...but no joy on actually getting line info or symbols for ff::ff_pipeline::ff_pipeline(bool,int,int,bool)!!

I can see symbols and line numbers/source code in my test.d file when using gdb (and other .d files), but not the .hpp files.

I can use tab to auto complete the ff::ff_pip, etc...so gdb seems to getting some symbol info together?? I can set the source directory and see a bunch of the files listed. Maybe that is the problem, though, because the .hpp files don't have any extension and are located in places like '/home/wilsonk/fastflow/cpp/ff/pipeline'??? That doesn't look right...does it?? Gdb looks like it is trying to find cpp_ff_pipeline.o is in that directory, instead of /home/wilsonk/fastflow???

Anyways, I can't seem to debug the the cpp code compiled with Calypso here. I suppose I am just missing something, or doing something wrong.

Syniurge commented 9 years ago

Elie, it looks like the 'this' at line 154 of pipeline.hpp is the problem.

I've been testing with a completely different version of FastFlow from yours, mine doesn't even have fftree_ptr and I can't find a version which does (googling fftree_ptr leads here to this issue).

Which one are you using?

wilsonk commented 9 years ago

svn info Path: . Working Copy Root Path: /home/wilsonk/Downloads/fastflow URL: https://svn.code.sf.net/p/mc-fastflow/code Relative URL: ^/ Repository Root: https://svn.code.sf.net/p/mc-fastflow/code Repository UUID: 5b2d11e7-8b83-46bf-b187-68a94c616a7d Revision: 282 Node Kind: directory Schedule: normal Last Changed Author: drocco Last Changed Rev: 282 Last Changed Date: 2015-03-06 08:06:57 -0700 (Fri, 06 Mar 2015)

Syniurge commented 9 years ago

Didn't you have an error about "treeLock" from fftree.hpp too? It was incorrectly skipped during the mapping.

Anyway the compilation works out of the box with the latest commits, now I'm looking into the segfault.

Syniurge commented 9 years ago

Anyways, the reason I am doing this manually is because I can't get gdb to find the line numbers/symbols for pipeline.hpp to save my life!!!

Got debug info working with this hack 3d0f8b647866cedfca50e8b122d3648c851c6593

Debugging Calypso executables is pretty smooth now!

wilsonk commented 9 years ago

I don't know what is going on now...newest updates from today and I get this:

cpp_ff_ff_pipeline.o:(.data.llvm.global_ctors33+0x0): multiple definition of `llvm.global_ctors33' __cpp_ff.o:(.data.llvm.global_ctors33+0x0): first defined here collect2: error: ld returned 1 exit status

I had this same error yesterday, but I thought it was something I might have been doing because I modified the example to mess with debugging. Now I have reverted to the pipeline code above and I am still getting this error?? I tried commenting out 'import (C++) ff._;' to no avail. Strange

I don't recall getting a treeLock errror.

Syniurge commented 9 years ago

Did you try removing the calypso_cache* files?

wilsonk commented 9 years ago

Yep, that is the first thing I always try.

Syniurge commented 9 years ago

The segfault is due to "roots" not being initialized by the global ctor, I guess now is a pretty good time to implement it to look at the other issue as well (for which I'll revert FastFlow to your revision).

Syniurge commented 9 years ago

Wut I wasn't even trying to fix your linking error and only just tried reverting FastFlow, and with the latest commit 1e46036bd5698259ef3798152b3cba76119e0424 the multiple definition error is gone.

wilsonk commented 9 years ago

Sweet, yep. The error is gone here also. I still get a segfault with the exe, but it does compile and new the fault is different here:

Program received signal SIGSEGV, Segmentation fault.
std::vector<std::pair<ff::fftree*, bool>, std::allocator<std::pair<ff::fftree*, bool> > >::push_back (this=0x8, __x=...)
    at /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:903
903     if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
(gdb) bt
#0  std::vector<std::pair<ff::fftree*, bool>, std::allocator<std::pair<ff::fftree*, bool> > >::push_back (this=0x8, __x=...)
    at /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:903
#1  0x0000000000421dec in ff::fftree::add_child (this=0x0, t=0x691500) at /home/wilsonk/Downloads/fastflow/ff/fftree.hpp:90
#2  0x0000000000406563 in ff::ff_pipeline::add_stage (this=0x7ffff7ec4e00, s=0x7ffff7ec4d10)
    at /home/wilsonk/Downloads/fastflow/ff/pipeline.hpp:198
#3  0x000000000040549c in _Dmain (args=...) at test.d:44
.
.
.
Syniurge commented 9 years ago

Solved!

EDIT: I forced a push to correct a mistake.

wilsonk commented 9 years ago

Arrrgggghhh, this is frustrating...the ctors problem is gone but that last commit does not fix the above segfault for me :(

And now I am getting a build error with my larger qt5 example also!

[/usr/include/qt5/QtGui/./qcolor.h(222)] opAssign ref anonymous(anonymous p) /usr/include/qt5/QtGui/./qcolor.h(222): Error: Global variable type does not match previous declaration with same mangled name: _D3cpp6QColor6QColor011anonymous6initZ

That error actually might have started a couple commits back.

Syniurge commented 9 years ago

I can't reproduce the segfault.. Are you using the same code you posted earlier: https://github.com/Syniurge/Calypso/issues/12#issuecomment-132451534 ?

In your backtrace it looks like fftree::children wasn't properly initialized, but how did it happen?.. ff_pipeline::fftree_ptr should have been initialized with new ff_pipeline(), for I even saw it correctly called when investigating the first segfault. I'll try again to be sure.

About the Qt5 error it seems caused by 93ffef59d7f310cd9e27e4c8b05c6880ff125338, sorry about that I'll look into it tomorrow.

Syniurge commented 9 years ago

fastflow step by step

I followed your test case step by step, saw everything called correctly, so no idea what's causing your segfault.

wilsonk commented 9 years ago

Sorry Ellie, my bad. Everything works fine for the pipeline code above. I was messing with the example and didn't get things back to the same code as above (I have about 5 different versions here for testing).

I'll check some other fastflow examples now and see how it goes. Looking good, thanks.

wilsonk commented 9 years ago

A few of the other examples I have all segfault at the same point here. It looks like a 'this' pointer problem again. The 'farm.run_and_wait_end()' (or similar) call is common across my examples like in the code below:

modmap (C++) "ff/farm.hpp";
modmap (C++) "ff/utils.hpp";
modmap (C++) "ff/node.hpp";
modmap (C++) "<vector>";
modmap (C++) "<iostream>";

import (C++) ff._;
import (C++) ff.ff_farm;
import (C++) ff.ff_node;
import (C++) ff.ff_buffernode;
import (C++) std.vector;
import (C++) std._;

import std.stdio, std.c.stdlib;

class Worker : ff_node {
   //alias svc = cpp.ff.ff_node.ff_node.svc;
   public:
        override void* svc(void * task) {
             int *t = cast(int*)task;
                 write("Worker ", ff_node.get_my_id());
             writeln(" received task ", *t);
                 return task;
    }
}

class Collector : ff_node {
   public:
        override void* svc(void* task) {
                 int *t = cast(int*)task;
                 if (*t == -1) return cast(void*)0;
                 return task;
        }
}
class Emitter : ff_node {
public:
    this(int max_task)
    {
        ntask = max_task;
    };

    void* svc(void* unused_task) {
    int* task = new int(ntask);
        --ntask;
    if (ntask<0)
           return cast(void*)0;
    return task;
    }
private:
    int ntask;
};

int main(char[][] args) {

    int nworkers = 1;
    int streamlen = 10;

    if (args.length > 1) {
        if (args.length != 3) {
            write("use: ", args[0]);
            writeln(" nworkers streamlen");
            return -1;
        }
        nworkers = 1;
        streamlen = 10;
    }

    if (!nworkers || !streamlen) {
        writeln("Wrong parameters values");
        return -1;
    }

    auto farm = new ff_farm!();
    auto E = new Emitter(streamlen);

    auto w = new vector!(ff_node *);
    for (int i=0; i<nworkers; i++)
    {
        ff_node* a = cast(ff_node*)new Worker();
        w.push_back(a);
    }
    farm.add_workers(*w);

    Collector C;
    farm.add_collector(cast(ff_node*)&C);

    if (farm.run_and_wait_end() < 0)
    {
        writeln("running farm error");
        return -1;
    }

    write("Done, time= ", farm.ffTime());
    writeln(" ms");

    auto stream = new ostream!char;
    farm.ffStats(*stream);

    return 0;
}

And that call ends up eventually calling 'this->spawn(filter?filter->getCPUId():-1)' in gt.hpp line 428. It would seem that there is a ctor construction problem in the parent class?

Syniurge commented 9 years ago

It's the cast which are causing problems because the base class offset is ignored. But you added them because there was an error, which I'm looking into.

Avoiding the cast gets you further, then there were a few more errors in Worker/Collector.svc methods and once these were fixed your example runs properly :

class Worker : ff_node {
        override void* svc(void * task) {
             int t = cast(int)task;
                 write("Worker ", ff_node.get_my_id());
             writeln(" received task ", t);
                 return task;
    }
}

class Collector : ff_node {
        override void* svc(void* task) {
                 int t = cast(int)task;
                 if (t == -1) return cast(void*)0;
                 return task;
        }
}

int main(char[][] args) {
    (...)

    for (int i=0; i<nworkers; i++)
    {
        ff_node* a = new Worker;
        w.push_back(a);
    }
    farm.add_workers(*w);

    auto C = new Collector;
    farm.add_collector(C);

    (...)
}

Démarrage : /home/syniurge/Projets/Calypso/tests/calypso/fastflow/ffdemo Worker 32343296 received task -3 Done, time= 0.044 ms

Syniurge commented 9 years ago

Wow, bad case of amnesia :astonished: :

Collector C;

is a reference, and &C a pointer to the reference, so besides being uninitialized casting it to ff_node* can't be correct because ff_node is a C++ class value type (yep D classes derived from C++ ones are still reference types, &C is more like ff_node**).

wilsonk commented 9 years ago

Ah, ok. It will be interesting to try to keep that straight in my head :)

The example works here now. Cool. I will try to translate some other examples now, especially to see what kind of performance we can get.

wilsonk commented 9 years ago

Woops, it wasn't quite working exactly right. I missed the add_emitter line in the example:

.
.
.
    auto E = new Emitter(streamlen);
    farm.add_emitter(E);

    auto w = new vector!(ff_node *);
.
.
.

Then we get several workers running but the output still isn't quite correct:

Worker 32089904 received task -180318224
Worker 32089904 received task -180318240
Worker 32089904 received task -180318256
Worker 32089904 received task -180318272
Worker 32089904 received task -180318288
Worker 32089904 received task -180318304
Worker 32089904 received task -180318320
Worker 32089904 received task -180318336
Worker 32089904 received task -180318352
Worker 32089904 received task -180318368
Done, time= 0.219 ms

When it should look like this:

Worker 0 received task 10
Worker 0 received task 9
Worker 0 received task 8
Worker 0 received task 7
Worker 0 received task 6
Worker 0 received task 5
Worker 0 received task 4
Worker 0 received task 3
Worker 0 received task 2
Worker 0 received task 1
DONE, time= 0.227 (ms)
FastFlow trace not enabled

Change the Worker.svc to use an int* again and we get 'task 10, 9, 8...'. Then one other small change to Worker.svc to use 'this.get_my_id();' and we get:

Worker 0 received task 10
Worker 0 received task 9
Worker 0 received task 8
Worker 0 received task 7
Worker 0 received task 6
Worker 0 received task 5
Worker 0 received task 4
Worker 0 received task 3
Worker 0 received task 2
Worker 0 received task 1
Done, time= 0.204 ms

Awesome. Just the bit of output from the trace is not seen. Remove the line that builds a new stream and just use 'farm.ffStats(cerr);' et voila, on a quelque chose passable. :)

Just being verbose here in case others run into similar errors/issues and would like to know how to work them out.

wilsonk commented 9 years ago

Nice, we have a complete working example of a multi-threaded quicksort directly translated from the FastFlow example (D-ified with a few changes). It runs in about the same time as a non-optimized C++ version...about 5% slower or so across 1-4 processors.

Here it is:

modmap (C++) "ff/farm.hpp";
modmap (C++) "ff/node.hpp";
modmap (C++) "<vector>";

import (C++) ff._;
import (C++) ff.ff_farm;
import (C++) ff.ff_node;
import (C++) ff.ff_buffernode;
import (C++) std.vector;
import (C++) std._;

import std.stdio, std.c.stdlib;
import std.random;

struct ff_task {
  int i,j,k;
}

__gshared uint size = 0;
__gshared int thresh = 0;
__gshared uint *A;

void print_array() {
     int j=0;
     for (uint i=0; i<size; i++) {
         if (j==15)
         {
            writeln();
            j=0;
         }
         j++;
         write(A[i]); write(" ");
     }
     writeln();writeln();
}

void swap(int i, int j) {
       int tmp;
       tmp = A[i], A[i] = A[j]; A[j] = tmp;
}

int FindPivot(int i, int j) {
    int pivot = (i+(j-i))/2;
    if (A[i]>A[pivot]) {
       if (A[i]>A[j]) return i;
       else return j;
    } else {
       if (A[pivot]>A[j]) return pivot;
       else return j;
    }
}

int Partition(int i, int j, uint pivot) {
    int left = i;
    int right = j;

    do {
        swap(left,right);
        while (A[left]  <  pivot) left++;
        while (A[right] >= pivot) right--;
    } while (left <= right);

    return(left);
}

void QuickSort(int i, int j) {
    if (j-i <= 1) {
       if (A[i]>A[j]) swap(i,j);
       return;
    }
    int pivot = FindPivot(i,j);
    int k     = Partition(i,j,A[pivot]);
    QuickSort(i, k-1);
    QuickSort(k,j);
}

void initArray() {
    /* All of the elements are unique. */
    for (uint i = 0; i < size; i++) A[i] = i;

    /* Shuffle them randomly. */
    for (uint i = 0; i < size; i++)
    swap(i, (uniform(0, size) % (size-i)) + i);
}

void usage() {
    write("Usage: ff_qs <sz> <threshold> <nworkers>\n\n");
    write("       sz                : size of unsorted array\n");
    write("       bubble-threashold : threashold for sequential sorting\n");
    write("       nworkers          : the n. of FastFlow worker threads\n");
}

class Worker : ff_node {
public:
    override void * svc(void * t) {
    ff_task * task = cast(ff_task*)t;
        int i=task.i, j=task.j;

        if (j - i <= thresh) {
            QuickSort(i,j);
                task.k = -1; // reset the value
            return task;
        }
        int pivot = FindPivot(i,j);
        task.k   = Partition(i,j,A[pivot]);

        return task;
    }
}

class Emitter : ff_node {
public:
    this () {streamlen = 0; }

    override void * svc(void * t) {
        ff_task * task = cast(ff_task*)t;
        if (task == null) {
            int pivot = FindPivot(0,size-1);
            int k     = Partition(0,size-1,A[pivot]);

            task = new ff_task;
            task.i=0; task.j=k-1;
            ff_send_out(task);

            task = new ff_task;
            task.i=k; task.j=size-1;
            ff_send_out(task);

            streamlen=2;

            return cast(void*)FF_GO_ON;
        }

        int i=task.i, j=task.j, k=task.k;
        --streamlen;
        if (k==-1) {
            if (streamlen == 0) {
                delete task;
                return null;
            }
            delete task;
            return cast(void*)FF_GO_ON;
        }

        task.i=i; task.j=k-1; task.k=-1;
        ff_send_out(task);

        task = new ff_task;
        task.i=k; task.j=j; task.k=-1;
        ff_send_out(task);

        streamlen +=2;

        return cast(void*)FF_GO_ON;
    }
private:
        uint streamlen;
};

int main(char[][] args) {

    if (args.length < 4 || args.length > 5)
    {
        usage();
        return -1;
    }

    size = atoi(&args[1][0]);
    thresh = atoi(&args[2][0]);
    int nworkers = atoi(&args[3][0]);

    uint tmp[] = new uint[size]; A = &tmp[0];

    initArray();

    auto farm = new ff_farm!();
    auto E = new Emitter();
    farm.add_emitter(E);

    auto w = new vector!(ff_node *);
    for (int i=0; i<nworkers; i++)
    {
        ff_node* a = new Worker();
        w.push_back(a);
    }
    farm.add_workers(*w);
    farm.wrap_around();

    writeln("starting...");
    if (farm.run_and_wait_end() < 0)
    {
        writeln("running farm error");
        return -1;
    }

    writeln("Time: ", farm.ffTime());
    //print_array();
    return 0;
}
Syniurge commented 9 years ago

Nice!

Feel free to make a PR, the more examples we have the better and the better I can test changes before a push.

I said I had an announcement coming soon since the Ogre3D demo is also working (or was.. not anymore since 93ffef59d7f310cd9e27e4c8b05c6880ff125338) but on a second thought I prefer to flesh things out so Calypso can really be considered partly usable.

wilsonk commented 9 years ago

Hmm, we have to turn off the gc with that quicksort code or we get a segfault on large sorts (around 5 million ints on my machine). I think it is the svc functions causing the problems...probably the pointer casting, I suppose.

Still getting similar sort times across 1-4 processors when the C++ version doesn't have optimizations turned on.

I get lots of undefined refs when I try to turn on any optimizations with Calypso? Not sure if there is a simple solution to that or not.