SoFunction
Updated on 2025-04-08

Methods to speed up the speed and efficiency of C# dual cycles

1. Preface

Recently, I used C# to rewrite a program I wrote before. When retrieving data assignment, I used the FOR loop structure. When the data volume is 90,000, the calculation amount is very large, which makes it time-consuming. Moreover, the calculation is completed and loaded into the chart and dataGridView chart controls at the same time (no third-party controls are used), so Baidu has many looping methods.

2. Speed ​​efficiency of multiple cycles

2.1. Comparison of common cycle speed efficiency

Circular structure Speed ​​and efficiency
for(int i=0;i<n;i++){} For iterations with known times, using for loops is usually the fastest.
foreach(string S in Data){} Suitable for traversing collections such as arrays or lists, but is usually slower than for loops because it requires more indirect access.
while
do-while
Suitable for iterations of uncertain times, but may not be as good as for loops in performance.


Parallel computing class, three methods block threads until all work is completed. Can only be used if applicable, otherwise unexpected errors will occur.
More efficient than
Suitable for performing large and no return value;
The use is disordered and is suitable for a large number of loop operations with indexes;
Suitable for loop execution of arrays, collections, and enumeration large data sets, the execution results are disordered.

2.2. How to improve the speed and efficiency of the cycle

2.2.1. Select the appropriate loop type

The efficiency of a cycle usually depends on multiple factors, including the type of cycle, the operation within the cycle body, and the number of cycles. It is important to choose the right type of loop.

2.2.2. Reduce operation in circulation

The less operations are performed in the circulation body, the higher the efficiency of the circulation. For example, avoid complex calculations or method calls in each iteration.

2.2.3. Use local variables instead of frequent access to members

If the loop needs to access the member of the object multiple times in the body, it is best to store the value of the member in a local variable, which can reduce the overhead of accessing the member multiple times.

2.2.4. Avoid unnecessary object creation or destruction in the circulation body

Creating a new object in each iteration increases the pressure on GC (garbage collection) and reduces performance. If possible, consider object pooling techniques or multiplexed objects.

2.2.5. Parallel processing can be used only when applicable

For computing tasks that can be processed in parallel, using or PLINQ (Parallel LINQ) can significantly improve performance, especially when processing large amounts of data. However, parallel processing needs to be used with caution as it increases thread management overhead.

3. Programming examples of how to improve the efficiency of double cycle computing speed

Following the above loop principle, for ordered data sets, for the for double loop is adopted by testing various loops multiple times.

In Hypack RAW data, the real-time and location of the positioning POS data collected in real time are mostly not synchronized with the EC1 data series, but are on the same route trajectory. The navigation distance and navigation speed are calculated through the time seconds, coordinates, and water depth of each collection point to calculate the location coordinates where the data collected by the interpolated EC1 is located.

POS data has time and positioning the east and north coordinate arrays, and POSTime[], POSEast[], and POSNorth[] data lengths are the same.
EC1 data has time and water depth arrays, and EC1Time[] and EC1WaterDepth[] have the same length.

The number of POS and EC1 data is not equal.

The water depth of the FIX collected data points must also be interpolated based on FIX and EC1 data, which will not be described here.

Hypack RAW data excerpts are as follows:

POS 0 37410.599 626291.650 3216635.552
QUA 0 37410.599 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37410.599 4 290361.17840 1121780.82570 9.91600 22330.60000
MSG 0 37410.538 $GPGGA,022330.60,2903.611784,N,11217.808257,E,2,08,1.0,9.916,M,0.0,M,8.0,0643*71
MSG 0 37410.581 $GPVTG,22.3,T,,M,0.08,N,0.15,K,P*23
MSG 0 37410.600 $GPZDA,022330.60,27,03,2024,00,00*62
EC1 1 37410.736 1.040
POS 0 37410.799 626291.660 3216635.561
QUA 0 37410.799 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37410.799 4 290361.17890 1121780.82630 9.90800 22330.80000
MSG 0 37410.735 $GPGGA,022330.80,2903.611789,N,11217.808263,E,2,08,1.0,9.908,M,0.0,M,8.0,0643*7A
MSG 0 37410.778 $GPVTG,42.9,T,,M,0.15,N,0.27,K,P*22
MSG 0 37410.797 $GPZDA,022330.80,27,03,2024,00,00*6C
EC1 1 37410.939 1.040
POS 0 37411.000 626291.673 3216635.574
QUA 0 37411.000 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37411.000 4 290361.17960 1121780.82710 9.89800 22331.00000
MSG 0 37410.938 $GPGGA,022331.00,2903.611796,N,11217.808271,E,2,08,1.0,9.898,M,0.0,M,8.0,0643*76
MSG 0 37410.981 $GPVTG,46.0,T,,M,0.17,N,0.31,K,P*2A
MSG 0 37411.001 $GPZDA,022331.00,27,03,2024,00,00*65
FIX 99 37411.109 48 626291.673 3216635.574
EC1 1 37411.138 1.030
POS 0 37411.200 626291.687 3216635.584
QUA 0 37411.200 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37411.200 4 290361.18010 1121780.82800 9.90800 22331.20000
MSG 0 37411.137 $GPGGA,022331.20,2903.611801,N,11217.808280,E,2,08,1.0,9.908,M,0.0,M,4.0,0643*7F
MSG 0 37411.180 $GPVTG,54.9,T,,M,0.21,N,0.39,K,P*2D
MSG 0 37411.199 $GPZDA,022331.20,27,03,2024,00,00*67
EC1 1 37411.337 1.020
POS 0 37411.400 626291.708 3216635.595
QUA 0 37411.400 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37411.400 4 290361.18070 1121780.82930 9.90400 22331.40000
MSG 0 37411.336 $GPGGA,022331.40,2903.611807,N,11217.808293,E,2,08,1.0,9.904,M,0.0,M,4.0,0643*71
MSG 0 37411.379 $GPVTG,57.9,T,,M,0.23,N,0.42,K,P*20
MSG 0 37411.398 $GPZDA,022331.40,27,03,2024,00,00*61
EC1 1 37411.538 1.030
POS 0 37411.599 626291.727 3216635.606
QUA 0 37411.599 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37411.599 4 290361.18130 1121780.83050 9.90100 22331.60000
MSG 0 37411.537 $GPGGA,022331.60,2903.611813,N,11217.808305,E,2,08,1.0,9.901,M,0.0,M,4.0,0643*7D
MSG 0 37411.580 $GPVTG,61.7,T,,M,0.22,N,0.41,K,P*29
MSG 0 37411.600 $GPZDA,022331.60,27,03,2024,00,00*63
EC1 1 37411.737 1.020
POS 0 37411.799 626291.750 3216635.618
QUA 0 37411.799 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37411.799 4 290361.18190 1121780.83190 9.89600 22331.80000
MSG 0 37411.736 $GPGGA,022331.80,2903.611819,N,11217.808319,E,2,08,1.0,9.896,M,0.0,M,4.0,0643*7B
MSG 0 37411.779 $GPVTG,64.7,T,,M,0.25,N,0.45,K,P*2F
MSG 0 37411.798 $GPZDA,022331.80,27,03,2024,00,00*6D
EC1 1 37411.937 1.030
POS 0 37412.000 626291.774 3216635.627
QUA 0 37412.000 7 3.000 1.000 8.000 2.000 0.000 0.000 0.000
RAW 0 37412.000 4 290361.18240 1121780.83340 9.89900 22332.00000
MSG 0 37411.937 $GPGGA,022332.00,2903.611824,N,11217.808334,E,2,08,1.0,9.899,M,0.0,M,4.0,0643*7E
MSG 0 37411.980 $GPVTG,68.6,T,,M,0.25,N,0.47,K,P*20
MSG 0 37411.999 $GPZDA,022332.00,27,03,2024,00,00*66
EC1 1 37412.138 1.020
POS 0 37412.200 626291.790 3216635.646

3.1. A regular double cycle

In the following code example, it is a dual loop in Hypack RAW data processing, interpolation of the positioning coordinates where EC1 collects the data.
The time and seconds of the data are arranged in an orderly manner from small to large.

	 for (int j = 0; j &lt; POSLeng; j++)
	 {
	     if (j == 0 &amp;&amp; EC1Time[i] &lt; POSTime[j])//EC1 time is less than the first POS time	     {
	         EC1East[i] = POSEast[j] - (POSEast[j + 1] - POSEast[j]) / (POSTime[j + 1] - POSTime[j]) * (POSTime[j] - EC1Time[i]);//The east coordinate of the water depth point	         EC1North[i] = POSNorth[j] - (POSNorth[j + 1] - POSNorth[j]) / (POSTime[j + 1] - POSTime[j]) * (POSTime[j] - EC1Time[i]);//North coordinate of water depth point	         break;
	     }
	     if (j == POSLeng - 1 &amp;&amp; EC1Time[i] &gt; POSTime[POSLeng - 1])//EC1 time is greater than the last POS time	     {
	         EC1East[i] = POSEast[j] + (POSEast[j] - POSEast[j - 1]) / (POSTime[j] - POSTime[j - 1]) * (EC1Time[i] - POSTime[j]);//The east coordinate of the water depth point	         EC1North[i] = POSNorth[j] + (POSNorth[j] - POSNorth[j - 1]) / (POSTime[j] - POSTime[j - 1]) * (EC1Time[i] - POSTime[j]);//North coordinate of water depth point	         break;
	     }
	     if (EC1Time[i] == POSTime[j])
	     {
	         EC1East[i] = POSEast[j];
	         EC1North[i] = POSNorth[j];
	         break;
	     }
	     if ((EC1Time[i] &gt; POSTime[j]) &amp;&amp; (EC1Time[i] &lt; POSTime[j + 1]))
	     {
	         EC1East[i] = POSEast[j] + (POSEast[j + 1] - POSEast[j]) / (POSTime[j + 1] - POSTime[j]) * (EC1Time[i] - POSTime[j]);//The east coordinate of the water depth point	         EC1North[i] = POSNorth[j] + (POSNorth[j + 1] - POSNorth[j]) / (POSTime[j + 1] - POSTime[j]) * (EC1Time[i] - POSTime[j]);//North coordinate of water depth point	         break;
	     }
	 }
	 SurveyEC1[i, 0] = EC1Time[i];
	 SurveyEC1[i, 1] = EC1East[i];
	 SurveyEC1[i, 2] = EC1North[i];
	 SurveyEC1[i, 3] = EC1WaterDepth[i];

The above code cycle efficiency is not high. During the test, when the RAW file is 3119 KB, the EC1 data volume reaches 97981, the POS data volume reaches 6142, and the FIX data volume reaches 503. Interpolate the coordinates of the EC1 acquisition point and retrieve the water depth collected by FIX from the EC1 acquisition point is very time-consuming, reaching about 2,000 milliseconds. After interpolation of the data, it must be loaded into the chart and dataGridView chart controls, and the loading icon reaches about 700 milliseconds, resulting in a very slow display.

3.2. Double cycle using improved dichotomy

In order to improve the computing speed and efficiency, a dual loop with improved dichotomy is adopted, and the code structure is optimized according to the loop principle, and the class encapsulated coordinate calculation loop is used to calculate the loop as an InterpCoord function, and the dichotomy is called it. Interpolate the coordinates of the EC1 acquisition point and the water depth collected by FIX, and the overall cycle is reduced to about 764 milliseconds.

  // Based on POS positioning data and time data, calculate the positioning coordinates of the interpolated EC1 water depth based on the speed  //Improve the dichotomy method and speed up the cycle  int EC1Leng = ;//The number of EC1 data  int MedianNum = 0;
  if ((EC1Leng &amp; 1) == 0)
  {
      MedianNum = EC1Leng / 2;//Even number, half of the data  }
  else
  {
      MedianNum = (int)(EC1Leng / 2) + 1; //Odd number, half of data + 1  }

  for (int i = 0; i &lt;= MedianNum; i++)//for (int i = 0; i &lt; EC1Leng; i++)
  {
      //From the minimum index of time seconds i = 0, calculate to the medium number in sequence      InterpCoord(EC1Time[i], ref EC1East[i], ref EC1North[i], POSTime, POSEast, POSNorth);
      SurveyEC1[i, 0] = EC1Time[i];
      SurveyEC1[i, 1] = EC1East[i];
      SurveyEC1[i, 2] = EC1North[i];
      SurveyEC1[i, 3] = EC1WaterDepth[i];

      //From the maximum index of time seconds i = EC1Leng - 1 Start the reverse order calculation to the medium number      int ReverseOrderNum = EC1Leng - i - 1;
      InterpCoord(EC1Time[ReverseOrderNum], ref EC1East[ReverseOrderNum], ref EC1North[ReverseOrderNum], POSTime, POSEast, POSNorth);

      //Combined into two-dimensional EC1 data      SurveyEC1[ReverseOrderNum, 0] = EC1Time[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 1] = EC1East[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 2] = EC1North[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 3] = EC1WaterDepth[ReverseOrderNum];
  }

Use class encapsulation coordinates to calculate loops as InterpCoord functions to construct references:

 public class Hypack
 {
        /// <summary> Calculate the coordinates of EC1 time point</summary>        /// <param name="InterpTime">EC1 time point</param>        /// <param name="InterpEast">EC1 East Coordinate</param>        /// <param name="InterpNorth">EC1 West Coordinate</param>        /// <param name="LineTime">POS time</param>        /// <param name="LineEast">POS East Coordinates</param>        /// <param name="LineNorth">POS West Coordinates</param>       public static void InterpCoord(double InterpTime, ref double InterpEast, ref double InterpNorth, double[] LineTime, double[] LineEast, double[] LineNorth)
       {
           int LineLeng = ;

           if (InterpTime &lt; LineTime[0])//EC1 time is less than the first POS online time, calculate the corresponding coordinates of EC1 time.           {
               InterpEast = LineEast[0] - (LineEast[1] - LineEast[0]) / (LineTime[1] - LineTime[0]) * (LineTime[0] - InterpTime);//The east coordinate of the water depth point               InterpNorth = LineNorth[0] - (LineNorth[1] - LineNorth[0]) / (LineTime[1] - LineTime[0]) * (LineTime[0] - InterpTime);//North coordinate of water depth point           }
           else if (InterpTime &gt; LineTime[LineLeng - 1])//EC1 time is greater than the last POS online time, calculate the corresponding coordinates of EC1 time.           {
               InterpEast = LineEast[LineLeng - 1] + (LineEast[LineLeng - 1] - LineEast[LineLeng - 2]) / (LineTime[LineLeng - 1] - LineTime[LineLeng - 2]) * (InterpTime - LineTime[LineLeng - 1]);//The east coordinate of the water depth point               InterpNorth = LineNorth[LineLeng - 1] + (LineNorth[LineLeng - 1] - LineNorth[LineLeng - 2]) / (LineTime[LineLeng - 1] - LineTime[LineLeng - 2]) * (InterpTime - LineTime[LineLeng - 1]);//North coordinate of water depth point           }
           else
           {
               for (int j = 0; j &lt; LineLeng; j++)
               {
                   if (InterpTime == LineTime[j])//EC1 time is equal to POS online time, and the coordinates are consistent.                   {
                      InterpEast = LineEast[j];
                      InterpNorth = LineNorth[j];
                      break;
                   }
                   if ((InterpTime &gt; LineTime[j]) &amp;&amp; (InterpTime &lt; LineTime[j + 1]))//EC1 time is between 2 times on the POS line, calculate the corresponding coordinates of EC1 time                   {
                      InterpEast = LineEast[j] + (LineEast[j + 1] - LineEast[j]) / (LineTime[j + 1] - LineTime[j]) * (InterpTime - LineTime[j]);//The east coordinate of the water depth point                      InterpNorth = LineNorth[j] + (LineNorth[j + 1] - LineNorth[j]) / (LineTime[j + 1] - LineTime[j]) * (InterpTime - LineTime[j]);//North coordinate of water depth point                      break;
                   }
               }
           }
     // Trying the following code is more time-consuming, which is equivalent to adding a loop, although the code is simple and simple      Find the index of the first element larger than the target
     //int indexGreaterThan = (LineTime, n =&gt; n &gt; InterpTime);
      Find equal totargetThe index of the first element
     //int indexEqualThan = (LineTime, n =&gt; n == InterpTime);
      Found the smallesttargetThe index of the first element
     //int indexLessThan = (LineTime, n =&gt; n &lt; InterpTime);
     //if (indexEqualThan &gt;= 0)
     //{
     //    InterpEast = LineEast[indexEqualThan];
     //    InterpNorth = LineNorth[indexEqualThan];
     //}

     //if (indexLessThan &gt;= 0 &amp;&amp; indexGreaterThan &gt;= 0)
     //{
     //    InterpEast = LineEast[indexLessThan] + (LineEast[indexGreaterThan] - LineEast[indexLessThan]) / (LineTime[indexGreaterThan] - LineTime[indexLessThan]) * (InterpTime - LineTime[indexLessThan]);//The east coordinate of the water depth point     //    InterpNorth = LineNorth[indexLessThan] + (LineNorth[indexGreaterThan] - LineNorth[indexLessThan]) / (LineTime[indexGreaterThan] - LineTime[indexLessThan]) * (InterpTime - LineTime[indexLessThan]);//North coordinate of water depth point     //}
       }
}

3.3. Adopt parallel loops

In order to improve faster computing speed and efficiency, parallel loops are adopted to optimize the code structure, and the coordinate calculation loop of the call class encapsulated is the InterpCoord function. Interpolate the coordinates of the EC1 acquisition point and the water depth collected by FIX, and the overall cycle is reduced to about 377 milliseconds.

   (0, EC1Leng, j =>
   {
        InterpCoord(EC1Time[j], ref EC1East[j], ref EC1North[j], POSTime, POSEast, POSNorth);
        SurveyEC1[j, 0] = EC1Time[j];
        SurveyEC1[j, 1] = EC1East[j];
        SurveyEC1[j, 2] = EC1North[j];
        SurveyEC1[j, 3] = EC1WaterDepth[j];
   });

4. A better way to double circulation

Improper multithreading can easily cause memory overflow, error handling and synchronization problems between threads. It is necessary to strictly avoid multiple threads accessing the same resource at the same time, otherwise it will lead to conflicts or errors. ‌There is memory destruction and improper processing will waste time and increase overhead. Other methods are still being explored...

The above is the detailed content of the method to speed up the efficiency of C# dual cycles. For more information about the speed of C# dual cycles, please pay attention to my other related articles!