0

I am building a program that deals with numbers in different bases, and I wanted to optimize it by using parallel programming, but I am new to all of it.

Right now, I am trying to implement a parallel Karatsuba multiplication algorithm:

    public static NX MulAK(NX A, NX B){
    // ¶ Safeguard:
    if(A.Base != B.Base){
        Console.Error.WriteLine("\tError:\nA multiplication of numbers with different bases was attempted!");
        return null!;
    }
    // ¶ Init:
    MatchLength(ref A, ref B);
    // Base Case:
    if(A.Len() == 1){return SingleMul(A, B[0]);}
    // ¶ Init:
    (NX A_L, NX A_H) = SplitHalf(A);
    (NX B_L, NX B_H) = SplitHalf(B);
    // ¶ Recursive calls in parallel:
    NX L;
    NX M;
    NX H;
    Parallel.Invoke(
        () => {L = MulAK(A_L,  B_L);},
        () => {M = MulAK(A_L + B_H, A_H + B_L);},
        () => {H = MulAK(A_H,  B_H);}
    );
    // Return:
    return 
        (L 
        + (M - L - H).ShiftPow(A.Len() / 2) 
        + H.ShiftPow(A.Len())
        ).ShiftPow(A.Powr + B.Powr);
}

It doesn't show any errors when calling the Parallel.Invoke(), but on the return it says:

Use of unassigned local variable 'L' [NumberX] csharp(CS0165)

...for L and all the others.

How can I make the recursive calls of the Karatsuba parallel to work, in my case?

1 Answers1

0

You get the error because the compiler cannot tell if L is definitely assigned or not. This variable is only assigned by the delegate that is given to Parallel.Invoke, but the compiler has no way of telling if Parallel.Invoke will actually call the delegate, so you will get an error.

Since you happen to know that it will actually do this, you can circumvent the error by assigning the variable: NX L = default(NX);

Note that your goal of 'optimizing' by running things in parallel will probably not give a positive result. Any kind of multi threading have some overhead, and if the amount of work is small, the overhead will dominate and result in much worse performance. Your algorithm is recursive, and does fairly little work in each recursion. After a few recursions you will have scheduled much more work than there are cores available to run it, so I would expect it to spend most time just switching between different tasks.

I would suggest that you instead start by profiling your code to check what takes most time. While you do not show the implementation of NX, it looks like some sort of list, and doing any kind of allocation inside tight loops are typically not helpful for performance. So I would expect that there might some other optimizations that would be much more helpful than simply running things in parallel.

Keep in mind, processors are amazingly fast. It is fairly rare that programs get close to the maximum throughput the processor can handle. The problems are much more often that poor algorithms are used, or that the program wastes most of the time doing unnecessary work. So picking an appropriate algorithm, and trying to minimize wasted time is often much more effective than doing work in parallel. Once you are confident you have done this, then you can think about parallelizing the work.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • I didn't provide additional information about NX because I deemed it unnecessary for this particular problem; I still have to test everything, but I just needed the proof of concept; even though this is already fixed for now, here is the GitHub repository, if you have any interest in how it works: https://github.com/Karuljonnai/NumberX Many thanks! – Karuljonnai Jan 26 '22 at 16:17