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

System.Threading.Parallel (experimental) #99

Open
wants to merge 35 commits into
base: master
Choose a base branch
from

Conversation

HydrogenC
Copy link
Contributor

@HydrogenC HydrogenC commented Mar 26, 2020

A very simple PPL implemention of System.Threading.Parallel, untested, may have bugs.
The only problem that cannot be solved is that I cannot get the pointer to each member of an IEnumerable. Otherwise, I can make Parallel.Foreach generic.
An approach to #85.

@bfiete
Copy link
Collaborator

bfiete commented Mar 26, 2020

You could have Foreach take a Span<T>, as both List<T> and T[] will implicitly convert.

@HydrogenC
Copy link
Contributor Author

Implemented with 6dcdf8d

@HydrogenC
Copy link
Contributor Author

HydrogenC commented Mar 26, 2020

The remaining problem now is that I can't refer to local variables in the functions as only delegates allow doing that. But I find it much more difficult to pass a delegate than a raw function pointer.

@HydrogenC
Copy link
Contributor Author

I tried to use delegate to replace function pointer, but it reported a link error on GetFuncPtr():

error LNK2019: public: static struct bf::System::ClassVData bf::CADNA::ForFunctionLong::sBfClassVData" (?sBfClassVData@ForFunctionLong@CADNA@bf@@2UClassVData@System@3@A)"public: static void __cdecl bf::CADNA::Program::Main(void)" (?Main@Program@CADNA@bf@@SAXXZ)
d:\ProgramData\CADNA\build\Debug_Win64\CADNA\CADNA.exe : fatal error LNK1120: 

@bfiete
Copy link
Collaborator

bfiete commented Mar 26, 2020

Hm.. I'll see if I can repro that link error. At any rate, GetFuncPtr will fail at runtime if the delegate contains any state (ie: 'this' pointer, captures)...

HOWEVER- what I would do is something like this... (totally untested)

struct ForeachState<T>
{
  delegate void(T) mDelegate;
  Span<T> mArgs;

  public static void Call(Self* state)
  {
      for (let arg in state->mArgs)
         state->mDelegate(arg);
  }
}

static extern void ForeachInternal(void* func, void* state);

public static void Foreach<T>(Span<T> arr, delegate void(T item) dlg)
{
  ForeachState<T> state;
  state.mDelegate = dlg;
  state.mArgs = args;

  function void(ForeachState<T>* state) func = => ForeachState<T>.Call;
  ForeachInternal(func, &state);
}

And then set up the concurrency::parallel_for_each to call a lambda that calls back through the func, passing state back through.

@HydrogenC
Copy link
Contributor Author

Thanks a lot, I will try it as soon as possible.

@bfiete
Copy link
Collaborator

bfiete commented Mar 26, 2020

Er- actually forget that. That doesn't actually work. The core idea of using a state struct to wrap up the delegate is the important part.

Your implementation of Parallel::ForeachInternal doesn't actually work, either. You must know the element size but you always assume the element size is sizeof(int). Something that WOULD work would be to keep encapsulating the 'args' in the state object as I did, and then use concurrency::parallel_for and pass the index back through the State.Call method and then just index the mArgs when passing through to the mDelegate...

The length of an Beef array is int32 on x86 and int64 on x64
Add parallel_invoke for 10+ functions
Fix arr type corresponding to 730faaf
Fix foreach not working
@HydrogenC
Copy link
Contributor Author

for (let arg in state->mArgs)
         state->mDelegate(arg);

Does beef actually support the "->" operator? I didn't find any thing about that in the docs.

@bfiete
Copy link
Collaborator

bfiete commented Mar 27, 2020

My mistake, my brain switched to C for a second there. Should just be a dot operator.

for (let arg in state.mArgs)
         state.mDelegate(arg);

The dot operator works for pointers to structs, there's no arrow operator.

@HydrogenC
Copy link
Contributor Author

Invoke and For works well, while Foreach causes access violation. I’ll fix it later.

@HydrogenC
Copy link
Contributor Author

HydrogenC commented Mar 28, 2020

Your implementation of Parallel::ForeachInternal doesn't actually work, either. You must know the element size but you always assume the element size is sizeof(int).

Actually, my previous implementation does work because I passed a array of pointers to the elements instead of the elements themselves, so I don't have to deal with element sizes in C++.

Something that WOULD work would be to keep encapsulating the 'args' in the state object as I did, and then use concurrency::parallel_for and pass the index back through the State.Call method.

This sounds great and is feasible for Lists and Arrays, but I plan to support non-sequence containers such as Dictionaries and Deques. Thus, I think that passing array of pointers is the only way to implement that. I will eventually make it usable for all containers implementing ICollection (but I noticed that Beef ICollection is not finished yet).

@HydrogenC
Copy link
Contributor Author

HydrogenC commented May 1, 2020

I Implemented Threading.Parallel for all platforms, but I would have to modify Dictionary<TKey, TValue> for a little bit to create a overload for dictionaries.

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

Successfully merging this pull request may close these issues.

None yet

2 participants