Member 12708425 Ответов: 2

Реализация параллельной обработки в коде C#


Я пытался написать один код, который должен извлекать старые значения, хранящиеся в какой-то собственной базе данных.
Для извлечения данных предусмотрены некоторые API и связанные с ними функции.Код выглядит следующим образом.

Теперь, если я запускаю этот код, он занимает почти 355 секунд. Я измерил время выполнения для одной точки, и это заняло почти 10 секунд(в среднем), чтобы сделать это.
Тест был проведен примерно с 37 такими "ПНТ", и потребовалось почти 355 секунд, чтобы получить необходимые значения.Мне нужно экспортировать то же самое в excel
тоже.Но экспорт в excel не занимает много времени по сравнению с извлечением данных из базы данных временных рядов с помощью предопределенных API.
У меня нет никакого контроля в API, и я ничего не могу сделать на том же самом.
Учитывая все это, могу ли я реализовать параллельную обработку.У меня есть машина с 24 ядрами процессора, где программа должна быть запущена.
Если кто-то может сказать мне, как реализовать то же самое в коде, это будет полезно для меня.
Я читал какую-то статью по этому поводу, но мне кажется, что это трудно реализовать в коде.
Если кто-то может помочь мне реализовать параллельную обработку в этом коде, это будет большим подспорьем в сокращении времени, затраченного программой.

Что я уже пробовал:

int i = 0, j = 0;
                string pnt = "";
                TimeSpan span;
                if (periodtime.SelectedIndex == 0)
                {
                    span = TimeSpan.FromSeconds(Convert.ToInt16(periodval.Text));
                }
                else if (periodtime.SelectedIndex == 1)
                {
                    span = TimeSpan.FromMinutes(Convert.ToInt16(periodval.Text));
                }
                else
                {
                    span = TimeSpan.FromHours(Convert.ToInt16(periodval.Text));
                }	
          	uint s = 0;
                double dval = 0;
                DateTime ts = DateTime.Now;
                string st = "";
                int nRet = 0;
foreach (string slpnt in pntid)
                {

//==========================================================================================
/*The point consist of a collection of strings separated by .,$ etc.It is defined inside a text file.The code reads the whole line and split a particular portion (required one) that we need to pass to API's function.*/
		  //That portion is stored in 'pnt' variable. 
                   pnt = slpnt.Split('-')[0].Split('$')[0];
                    i = 0;
                   span = TimeSpan.FromHours(24);
                   //GetHistMin is a function(Part of an application API) to fetch historical minimum values stored historixcal information database.Hence if .nRet =0 , it enters the loop.
//Here the time range is from Convert.ToDateTime(Fdt.Text + " 00:00:00" =00:00:00 hrs to 24:00:00 hrs (Convert.ToDateTime(Fdt.Text + " 00:00:00").AddHours(24)
/*Span is user given  and here it is 30 seconds.Please note that it is a large data base and for 24 hrs we approximately have to search 4000 samples for a pnt variable and get the minimum out of it.We need to fetch such 50 pnt in minimum to get the desired result.*/
nRet = History.GetHistMin(pnt, Convert.ToDateTime(Fdt.Text + " 00:00:00"), Convert.ToDateTime(Fdt.Text + " 00:00:00").AddHours(24), span, out s);
                   if (nRet == 0)
                   {
                       nRet = History.GetNextHist(s, out dval, out  ts, out st);
					   //GetNextHist is used to collect the resulting value the returned by GetHistMin(). 
                       if (j == 0)
                       {
//Doing the required calculation and adding it to new row of data table.
                           dr = dt.NewRow();
                           dr[0] = "Minimum(of the day)";
                           if (slpnt.Contains("$"))
                           {
                           dr["C" + j.ToString()] = Math.Round(dval, 2);

                           }
                           else
                           {
                           dr["C" + j.ToString()] = Math.Round(dval, 1);
                           }
                           dt.Rows.Add(dr);
                         }
                       else
                 {
                           if (slpnt.Contains("$"))
                 {
                dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 2);
                 }
                           else
                 {
                dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 1);
                 }
             }
         } 


//==========================================================
//Like Minimum, it is calculating the Maximum of the day value.
                  nRet = History.GetHistMax(pnt, Convert.ToDateTime(Fdt.Text + " 00:00:00"), Convert.ToDateTime(Fdt.Text + " 00:00:00").AddHours(24), span, out s);
                   if (nRet == 0)
                   {

                       nRet = History.GetNextHist(s, out dval, out  ts, out st);
                       if (j == 0)
                       {
                           dr = dt.NewRow();
                           dr[0] = "Maximum(of the day)";
                           if (slpnt.Contains("$"))
                           {
                               dr["C" + j.ToString()] = Math.Round(dval, 2);
                           }
                           else
                           {
                               dr["C" + j.ToString()] = Math.Round(dval, 1);
                           }
                           dt.Rows.Add(dr);
                           }
                       else
                       {
                           if (slpnt.Contains("$"))
                           {
         dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 2);
                           }
                           else
                           {
         dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 1);
                           }
                        }			   
                    }  

//=========================================================================================================================
//Like Minimum, it is calculating the Average of the day value.
                    nRet = History.GetHistAvg(pnt, Convert.ToDateTime(Fdt.Text + " 00:00:00"), Convert.ToDateTime(Fdt.Text + " 00:00:00").AddHours(24), span, out s);
                    
                    if (nRet == 0)
                    {
                        nRet = History.GetNextHist(s, out dval, out  ts, out st);
                        if (j == 0)
                        {
                            dr = dt.NewRow();
                            dr[0] = "Average(of the day)";
                            if (slpnt.Contains("$"))
                            {
                                dr["C" + j.ToString()] = Math.Round(dval, 2);
                            }
                            else
                            {
                                dr["C" + j.ToString()] = Math.Round(dval, 1);
                            }
                            dt.Rows.Add(dr);
                        }
                        else
                        {
                            if (slpnt.Contains("$"))
                            {
              dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 2);
                            }
                            else
                            {
              dt.Rows[dt.Rows.Count - 1]["C" + j.ToString()] = Math.Round(dval, 1);
                            }
                        }
                    }
	j++;
}

Bernhard Hiller

Вы уверены, что правильно звонить History.GetNextHist независимо от того, вызываете ли вы GetHistMin, GetHistMax или GetHistAvg непосредственно перед этим?

2 Ответов

Рейтинг:
2

Bernhard Hiller

Я сомневаюсь, что вы можете ускорить процедуру с помощью распараллеливания: это медленная обработка на другой стороне, которая потребляет время. Я думаю, что параллельные запросы сделают отдельные запросы медленнее, потому что эта другая сторона все равно находится на своих границах.


Member 12708425

Не могли бы вы помочь мне протестировать код, реализующий параллельную обработку?Позвольте мне попробовать один раз.У меня не было никакого другого выбора, со мной, чем этот.

Рейтинг:
1

phil.o

Было бы полезно также посмотреть урок истории. Есть некоторые оптимизации, которые можно сделать на уже показанном коде:

- Второй блок: span переменная влияет на стандартное значение, которое перезаписывает то, которое было затронуто в первом блоке (перед каждым циклом for). Это нарочно?

- Второй блок: я бы предложил использовать DateTime.TryParse вместо Convert.ToDateTime (Класс Convert обычно имеет ужасную производительность). И кэшируйте результат перед тем, как GetHistMin вызовите так, чтобы он не вычислялся дважды на каждой итерации. Что-то вроде:

DateTime fdtTime;
if (DateTime.TryParse(Fdt.Text, out fdtTime)) {
   nRet = History.GetHistMin(pnt, fdtTime, fdtTime.AddHours(24), span, out s);
  // ...
}

В общем случае, когда вы видите некоторый код, который повторяет одно и то же вычисление несколько раз в одном и том же логическом блоке, тогда может быть полезно кэшировать результат в переменной.
TryParse метод имеет несколько перегрузок, что позволяет иметь тонкий контроль над процессом (в основном связанным с культурой).
Нет необходимости добавлять 00:00:00 время, это избавляет вас от большого количества операций копирования строк.

- Третий и четвертый блоки: одно и то же замечание для обработки переменных DateTime в GetHistMax и GetHistAvg методы.

Там могут быть возможные оптимизации в другом месте, вы должны попытаться увидеть, где виновник, когда вы выполняете свой код (то есть, где процесс тратит большую часть своего времени); это даст вам представление о том, что действительно должно быть оптимизировано.


Member 12708425

Я сделал модификацию и использовал Tryparse, но это занимает столько же времени.Кроме того, у меня нет кода для методов GetHistMIn/Max/Avg

phil.o

Затем вам нужно найти, где обработка занимает большую часть своего времени:
Руководство для начинающих по профилированию производительности

Member 12708425

Я обнаружил,что методы GetHistMin, GetHistAvg и GetHistMax требуют времени.Среднее время обработки для получения 24 часов составляет почти 3 секунды для каждого из этих методов.например, GetHistMin проверяет образцы через 24 часа( почти 4000 значений для каждой точки через 24 часа.)
и получите минимальное двойное значение из 4000 образцов. Это для одной точки и для минимального метода.Аналогично для метода Max и Avg и именно для таких 37 баллов. Вот почему я думаю о параллельном программировании.Может ли это быть реализовано здесь?