Napisana metoda przyspiesza magicznie po "rozbiciu" jej na kilka z innymi parametrami

0

Witam serdecznie,

Dzisiaj mam do Was forumowicze (drodzy ) następne pytanie z cyklu: O CO KAMAN DO CHOLERY! Otóż w walce z moja pracą inżynierską (optymalizacja metod numerycznych przy wykorzystania wielowątkowości), przepisywałem wraz z promotorem "model matematyczny", z języka c++ do C#, program oblicza kilka macierzy, przekształca je dodaje, mnoży itp... to nie jest w tym momencie tak istotne. Istotna natomiast jest taka sprawa ze napisałem metodę do której przekazuje dwa parametry (które są zmiennymi int) parametry te określają "i" dla pętli for czyli jej start (N1) i koniec (N2) maksymalnie pętla iteruje od 1 do 23 (to są elementy 25-cio elementowego modułu).

Problem polega na tym że kiedy wywołuje metodą w ten sposób:

Obliczenia_A(1,23);

Czas wychodzi mi ok 6s, natomiast jeżeli rozbije ta petle na:

 Obliczenia_A(1,5);
Obliczenia_A(6,11);
Obliczenia_A(12,18);
Obliczenia_A(19,23);

To czas "MAGICZNIE" skraca się do ok 1-1,5s??? O co chodzi nie stosuje jeszcze wielowątkowości a aplikacja już niby przyspiesza, dodam ze przy pierwszym i drugim sposobie zwraca mi prawidłowe wyniki, a promotor naciska mnie że po co w takim razie wielowątkowość??? Sprawdzałem timerami i zwalnia mi ta metoda przy zerowaniu macierzy (tablicy) trójelementowej ale wg. mojej oceny jest to bez sensu :/

int i, i1, i2, k, j;

            //Obliczanie macierzy A (metoda interpolacji Eulera)
            for (int N = N1; N <= N2; N++)
            {
                for (i = 1; i <= 3; i++)
                {
                    matrixA[i, i, N] = 1.0 / (daneRL[i + 3 + (N - 1) * 6] + dt * daneRL[i + (N - 1) * 6]);
                }
                for (i = 4; i <= 6; i++)
                {
                    matrixA[i, i, N] = matrixA[i - 3, i - 3, N];
                }
                matrixA[4, 1, N] = -matrixA[1, 1, N];
                matrixA[5, 2, N] = -matrixA[2, 2, N];
                matrixA[6, 3, N] = -matrixA[3, 3, N];
                matrixA[1, 4, N] = matrixA[4, 1, N];
                matrixA[2, 5, N] = matrixA[5, 2, N];
                matrixA[3, 6, N] = matrixA[6, 3, N];

                for (i = N1; i <= N2; i++)                                //W tym miejscu zwalnia... 
                {
                    for (i1 = 1; i1 <= 38; i1++)
                    {
                        for (i2 = 1; i2 <= 6; i2++)
                        {
                            matrixPA[i1, i2, i] = 0.0;
                        }
                    }
                    for (j = 1; j <= macP[i, 0]; j++)
                    {
                        i1 = (macP[i, j] - 1) / 6 + 1;
                        i2 = macP[i, j] - (i1 - 1) * 6;
                        for (k = 1; k <= 6; k++)
                        {
                            matrixPA[i1, k, i] = matrixPA[i1, k, i] + matrixA[i2, k, i];
                        }
                    }
                }

                for (i = N1; i <= N2; i++)
                {
                    for (i1 = 1; i1 <= 38; i1++)
                    {
                        for (i2 = 1; i2 <= 38; i2++)
                        {
                            matrixPAP[i1, i2, i] = 0.0;
                        }
                    }
                    for (j = 1; j <= macP[i, 0]; j++)
                    {
                        i1 = (macP[i, j] - 1) / 6 + 1;
                        i2 = macP[i, j] - (i1 - 1) * 6;
                        for (k = 1; k <= 38; k++)
                        {
                            matrixPAP[k, i1, i] = matrixPAP[k, i1, i] + matrixPA[k, i2, i];
                        }
                    }
                }
            }           
            return matrixPAP;
        } 

Proszę o jakąś podpowiedź, myslałem że kompilator sprowadza wszytsko do binarki i czy odpale pare metod z innymi parametrami czy jedna z calym "zakresem" to i tak czas powinien byc bardzo podobny.

Pozdrawiam, jak by ktoś sie zainteresował tym tematem to mogę udostępnić jeszcze jakieś informacje.

0

Nie chce mi się dokładnie analizować kodu, ale na pewno wyniki są takie same? Szczególnie drugie zagnieżdżenie pętli od N1 do N2 (zakładając, że to są te dwa parametry funkcji) może spowodować, że wynik jest różny. Ten kod to ogólnie brzydki jest.

0

Nie przychodzi mi żadna odpowiedź do głowy, jeżeli chodzi o ten konkretny przykład, ale jeżeli chodzi o przetwarzanie tablic w C# mam trzy rady:

  • tablice wielowymiarowe są stosunkowo wolne w C#; użyj jagged-arrays (tablice tablic) bądź wrappera (którego JIT rozwinie) na tablicę jednowymiarową
  • w przetwarzaniu tablic jedną z najbardziej czasochłonnych operacji to sprawdzanie zakresów (bound-checking); JIT pomija je w przypadku pętli po całej tablicy (od 0 do Length - 1)
  • kompilacja pod x86 i x64 potrafi dać czasem zaskakująco różne wyniki (w obie strony), między innymi dzięki innym optymalizacjom
0
Zjarek napisał(a)

Nie chce mi się dokładnie analizować kodu, ale na pewno wyniki są takie same? Szczególnie drugie zagnieżdżenie pętli od N1 do N2 (zakładając, że to są te dwa parametry funkcji) może spowodować, że wynik jest różny. Ten kod to ogólnie brzydki jest.

Kod może do za pieknych nie nalezy, mozna jeszcze w nim duzo poprawić, ale tak miałem napisać i tak jest. Co do wyników to tak sa IDENTYCZNE

0

W przypadku wywołania Obliczenia_A(1,23) pętla z licznikiem N wykona się 23 razy a w niej po 223 razy pętle z licznikiem i. Daje to razem 1058 razy.
W drugim przypadku 2
525 + 727 + 525 co razem daje 218.

EDIT: Późno już.

0

Po pierwsze wywal tą zewnętrzną pętlę z funkcji (ogólnie wszystkie po N). Zrób funkcję tylko dla jednej macierzy, przetestuj ją dobrze, dopiero później ewentualnie opakuj w zewnętrzną. Co do kwestii stylistycznych to ten kod jest brzydki i może Ci jeszcze dużo problemów przynieść. Po co Ci tablice trójwymiarowe, spokojnie da się to zrobić na dwuwymiarowych i kod będzie o wiele prostszy. Jest to nie tylko kwestia stylistyczna, ale w tym przypadku także wydajności. Dodatkowo zmienne powinny być tworzone jak najbliżej użycia, ewentualnie przed ciasną pętlą, popraw szczególnie nazewnictwo indeksów w pętlach for oraz ilość zmiennych globalnych. Czym się różni przedrostek matrix od Mac.

0

Jeśli N1 i N2 to parametry w metodzie Obliczenia_A to ta metoda ma kwadratową złożoność od zakresu tych liczb. W takim przypadku najlepiej wywołać:
Obliczenia_A(1, 2);
Obliczenia_A(2, 3);
...
Obliczenia_A(22, 23);
Wtedy wydajność powinna być największa. Jeżeli wyniki dostajesz takie same to znaczy, że gdzieś iterujesz więcej razy niż potrzeba, w międzyczasie gubiąc wyniki i licząc od nowa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1