欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

加快C#雙循環(huán)的速度效率的方法

 更新時(shí)間:2025年02月20日 08:38:55   作者:中游魚(yú)  
最近使用 C# 改寫(xiě)以前編的一個(gè)程序,檢索數(shù)據(jù)賦值時(shí),使用 FOR 循環(huán)結(jié)構(gòu),當(dāng)數(shù)據(jù)量在9萬(wàn)條時(shí),計(jì)算量很大,導(dǎo)致很耗時(shí),何況計(jì)算完成同時(shí)加載到 chart 和 dataGridView 圖表控件中(沒(méi)有使用第三方控件),于是百度了很多循環(huán)手段,本文介紹了加快C#雙循環(huán)的速度效率的方法

一、前言

最近使用 C# 改寫(xiě)以前編的一個(gè)程序,檢索數(shù)據(jù)賦值時(shí),使用 FOR 循環(huán)結(jié)構(gòu),當(dāng)數(shù)據(jù)量在9萬(wàn)條時(shí),計(jì)算量很大,導(dǎo)致很耗時(shí),何況計(jì)算完成同時(shí)加載到 chart 和 dataGridView 圖表控件中(沒(méi)有使用第三方控件),于是百度了很多循環(huán)手段。

二、多種循環(huán)的速度效率

2.1、常用循環(huán)速度效率對(duì)比

循環(huán)結(jié)構(gòu)體速度和效率
for(int i=0;i<n;i++){}對(duì)于已知次數(shù)的迭代,使用 for 循環(huán)通常是最快的。
foreach(string S in Data){}適用于遍歷集合,如數(shù)組或列表,但通常比 for 循環(huán)慢,因?yàn)樗枰嗟拈g接訪問(wèn)。
while
do-while
適用于不確定次數(shù)的迭代,但可能在性能上不如 for 循環(huán)。
Parallel.For
Parallel.ForEach
Parallel.Invoke
并行計(jì)算類(lèi),三個(gè)方法都會(huì)阻塞線程直到所有工作完成為止。僅當(dāng)適用時(shí)可采用,否則出現(xiàn)意外錯(cuò)誤。
Parallel.ForEach 比 Parallel.For 效率更高。
Parallel.Invoke 適合用于執(zhí)行大量且無(wú)返回值的場(chǎng)景;
Parallel.For 使用是無(wú)序的,適合帶索引的大量循環(huán)操作;
Parallel.ForEach 適合數(shù)組、集合、枚舉大數(shù)據(jù)集的循環(huán)執(zhí)行,執(zhí)行結(jié)果是無(wú)序的。

2.2、如何提高循環(huán)的速度效率

2.2.1、選擇合適的循環(huán)類(lèi)型

循環(huán)的效率通常取決于多個(gè)因素,包括循環(huán)的類(lèi)型、循環(huán)體內(nèi)的操作、以及循環(huán)的次數(shù)。選擇合適的循環(huán)類(lèi)型很重要。

2.2.2、減少循環(huán)體內(nèi)的操作

循環(huán)體內(nèi)的操作越少,循環(huán)的效率越高。例如,避免在每次迭代中進(jìn)行復(fù)雜的計(jì)算或方法調(diào)用。

2.2.3、使用局部變量而非頻繁訪問(wèn)成員

如果循環(huán)體內(nèi)需要多次訪問(wèn)對(duì)象的成員,最好將該成員的值存儲(chǔ)在局部變量中,這樣可以減少多次訪問(wèn)成員的開(kāi)銷(xiāo)。

2.2.4、避免在循環(huán)體內(nèi)進(jìn)行不必要的對(duì)象創(chuàng)建或銷(xiāo)毀

每次迭代創(chuàng)建新對(duì)象會(huì)增加GC(垃圾回收)的壓力,降低性能。如果可能,考慮對(duì)象池技術(shù)或復(fù)用對(duì)象。

2.2.5、僅當(dāng)適用時(shí)可采用并行處理

對(duì)于可以并行處理的計(jì)算任務(wù),使用 Parallel.For 或 PLINQ(Parallel LINQ)可以顯著提高性能,特別是當(dāng)處理大量數(shù)據(jù)時(shí)。然而,并行處理需要謹(jǐn)慎使用,因?yàn)樗鼤?huì)增加線程管理的開(kāi)銷(xiāo)。

三、如何提高雙循環(huán)計(jì)算速度效率的編程實(shí)例

遵循上述循環(huán)原則,對(duì)于有序的數(shù)據(jù)集,通過(guò)多次測(cè)試各種循環(huán),采用的 for 雙循環(huán)。

Hypack RAW 數(shù)據(jù)中,實(shí)時(shí)采集的定位 POS 數(shù)據(jù)和 EC1 數(shù)據(jù)系列,時(shí)間和位置大部分不同步,但處于同一個(gè)航線軌跡上,通過(guò)各采集點(diǎn)的時(shí)間秒、坐標(biāo)、水深,計(jì)算航行距離、航行速度,以計(jì)算插補(bǔ) EC1 采集數(shù)據(jù)所在定位坐標(biāo)。

POS 數(shù)據(jù)有時(shí)間、定位東和北坐標(biāo)數(shù)組,POSTime[]、POSEast[]、POSNorth[] 數(shù)據(jù)長(zhǎng)度一致。
EC1 數(shù)據(jù)有時(shí)間、水深數(shù)組,EC1Time[]、EC1WaterDepth[] 數(shù)據(jù)長(zhǎng)度一致。

POS 和 EC1 數(shù)據(jù)個(gè)數(shù)不對(duì)等。

還要根據(jù) FIX 和 EC1 數(shù)據(jù),插補(bǔ) FIX 采集數(shù)據(jù)點(diǎn)的水深,這里就不描述。

Hypack RAW 數(shù)據(jù)節(jié)選如下:

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、中規(guī)中矩的雙循環(huán)

以下代碼實(shí)例中,是 Hypack RAW 數(shù)據(jù)處理中的雙循環(huán),插補(bǔ) EC1 采集數(shù)據(jù)所在定位坐標(biāo)。
數(shù)據(jù)的時(shí)間秒從小到大有序排列。

	 for (int j = 0; j < POSLeng; j++)
	 {
	     if (j == 0 && EC1Time[i] < POSTime[j])//EC1時(shí)間小于第一個(gè)POS時(shí)間
	     {
	         EC1East[i] = POSEast[j] - (POSEast[j + 1] - POSEast[j]) / (POSTime[j + 1] - POSTime[j]) * (POSTime[j] - EC1Time[i]);//水深點(diǎn)東坐標(biāo)
	         EC1North[i] = POSNorth[j] - (POSNorth[j + 1] - POSNorth[j]) / (POSTime[j + 1] - POSTime[j]) * (POSTime[j] - EC1Time[i]);//水深點(diǎn)北坐標(biāo)
	         break;
	     }
	     if (j == POSLeng - 1 && EC1Time[i] > POSTime[POSLeng - 1])//EC1時(shí)間大于最后一個(gè)POS時(shí)間
	     {
	         EC1East[i] = POSEast[j] + (POSEast[j] - POSEast[j - 1]) / (POSTime[j] - POSTime[j - 1]) * (EC1Time[i] - POSTime[j]);//水深點(diǎn)東坐標(biāo)
	         EC1North[i] = POSNorth[j] + (POSNorth[j] - POSNorth[j - 1]) / (POSTime[j] - POSTime[j - 1]) * (EC1Time[i] - POSTime[j]);//水深點(diǎn)北坐標(biāo)
	         break;
	     }
	     if (EC1Time[i] == POSTime[j])
	     {
	         EC1East[i] = POSEast[j];
	         EC1North[i] = POSNorth[j];
	         break;
	     }
	     if ((EC1Time[i] > POSTime[j]) && (EC1Time[i] < POSTime[j + 1]))
	     {
	         EC1East[i] = POSEast[j] + (POSEast[j + 1] - POSEast[j]) / (POSTime[j + 1] - POSTime[j]) * (EC1Time[i] - POSTime[j]);//水深點(diǎn)東坐標(biāo)
	         EC1North[i] = POSNorth[j] + (POSNorth[j + 1] - POSNorth[j]) / (POSTime[j + 1] - POSTime[j]) * (EC1Time[i] - POSTime[j]);//水深點(diǎn)北坐標(biāo)
	         break;
	     }
	 }
	 SurveyEC1[i, 0] = EC1Time[i];
	 SurveyEC1[i, 1] = EC1East[i];
	 SurveyEC1[i, 2] = EC1North[i];
	 SurveyEC1[i, 3] = EC1WaterDepth[i];

以上代碼循環(huán)效率不高,測(cè)試中,當(dāng) RAW 文件 3119 KB,EC1 數(shù)據(jù)量達(dá)到 97981 個(gè),POS數(shù)據(jù)量達(dá)到 6142 個(gè),F(xiàn)IX數(shù)據(jù)量達(dá)到 503個(gè)。插補(bǔ) EC1 采集點(diǎn)的坐標(biāo)和 從 EC1 采集點(diǎn)檢索 FIX 采集的水深,很耗時(shí),達(dá)到 2 千毫秒左右,插補(bǔ)數(shù)據(jù)后,還要加載到 chart 和 dataGridView 圖表控件中,加載圖標(biāo)達(dá)到 7 百毫秒左右,導(dǎo)致顯示很慢。

3.2、采用改進(jìn)二分法的雙循環(huán)

為了提高運(yùn)算速度和效率,采用改進(jìn)二分法的雙循環(huán),根據(jù)循環(huán)原則,優(yōu)化代碼結(jié)構(gòu),使用類(lèi)封裝坐標(biāo)計(jì)算循環(huán)為 InterpCoord 函數(shù),二分法循環(huán)調(diào)用它。插補(bǔ) EC1 采集點(diǎn)的坐標(biāo)和 FIX 采集的水深,整體循環(huán)減小到 764 毫秒左右。

  //根據(jù) POS 定位數(shù)據(jù)和時(shí)間數(shù)據(jù),以航速計(jì)算插補(bǔ) EC1 水深的定位坐標(biāo)
  //改進(jìn)二分法,加快循環(huán)
  int EC1Leng = ListEc1Time.Length;//EC1數(shù)據(jù)個(gè)數(shù)
  int MedianNum = 0;
  if ((EC1Leng & 1) == 0)
  {
      MedianNum = EC1Leng / 2;//偶數(shù),數(shù)據(jù)半數(shù)
  }
  else
  {
      MedianNum = (int)(EC1Leng / 2) + 1; //奇數(shù),數(shù)據(jù)半數(shù)+1
  }

  for (int i = 0; i <= MedianNum; i++)//for (int i = 0; i < EC1Leng; i++)
  {
      //從時(shí)間秒最小索引 i = 0 開(kāi)始順序計(jì)算到中數(shù)
      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];

      //從時(shí)間秒最大索引 i = EC1Leng - 1 開(kāi)始逆序計(jì)算到中數(shù)
      int ReverseOrderNum = EC1Leng - i - 1;
      InterpCoord(EC1Time[ReverseOrderNum], ref EC1East[ReverseOrderNum], ref EC1North[ReverseOrderNum], POSTime, POSEast, POSNorth);

      //組合為二維 EC1 數(shù)據(jù)
      SurveyEC1[ReverseOrderNum, 0] = EC1Time[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 1] = EC1East[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 2] = EC1North[ReverseOrderNum];
      SurveyEC1[ReverseOrderNum, 3] = EC1WaterDepth[ReverseOrderNum];
  }

使用類(lèi)封裝坐標(biāo)計(jì)算循環(huán)為 InterpCoord 函數(shù),以便構(gòu)造引用:

 public class Hypack
 {
        /// <summary>計(jì)算 EC1 時(shí)間點(diǎn)的坐標(biāo)</summary>
        /// <param name="InterpTime">EC1時(shí)間點(diǎn)</param>
        /// <param name="InterpEast">EC1東坐標(biāo)</param>
        /// <param name="InterpNorth">EC1西坐標(biāo)</param>
        /// <param name="LineTime">POS時(shí)間點(diǎn)</param>
        /// <param name="LineEast">POS東坐標(biāo)</param>
        /// <param name="LineNorth">POS西坐標(biāo)</param>
       public static void InterpCoord(double InterpTime, ref double InterpEast, ref double InterpNorth, double[] LineTime, double[] LineEast, double[] LineNorth)
       {
           int LineLeng = LineTime.Length;

           if (InterpTime < LineTime[0])//EC1時(shí)間小于第一個(gè) POS 線上時(shí)間,計(jì)算EC1時(shí)間對(duì)應(yīng)坐標(biāo)
           {
               InterpEast = LineEast[0] - (LineEast[1] - LineEast[0]) / (LineTime[1] - LineTime[0]) * (LineTime[0] - InterpTime);//水深點(diǎn)東坐標(biāo)
               InterpNorth = LineNorth[0] - (LineNorth[1] - LineNorth[0]) / (LineTime[1] - LineTime[0]) * (LineTime[0] - InterpTime);//水深點(diǎn)北坐標(biāo)
           }
           else if (InterpTime > LineTime[LineLeng - 1])//EC1時(shí)間大于最后一個(gè) POS 線上時(shí)間,計(jì)算EC1時(shí)間對(duì)應(yīng)坐標(biāo)
           {
               InterpEast = LineEast[LineLeng - 1] + (LineEast[LineLeng - 1] - LineEast[LineLeng - 2]) / (LineTime[LineLeng - 1] - LineTime[LineLeng - 2]) * (InterpTime - LineTime[LineLeng - 1]);//水深點(diǎn)東坐標(biāo)
               InterpNorth = LineNorth[LineLeng - 1] + (LineNorth[LineLeng - 1] - LineNorth[LineLeng - 2]) / (LineTime[LineLeng - 1] - LineTime[LineLeng - 2]) * (InterpTime - LineTime[LineLeng - 1]);//水深點(diǎn)北坐標(biāo)
           }
           else
           {
               for (int j = 0; j < LineLeng; j++)
               {
                   if (InterpTime == LineTime[j])//EC1時(shí)間等于 POS 線上時(shí)間,坐標(biāo)一致
                   {
                      InterpEast = LineEast[j];
                      InterpNorth = LineNorth[j];
                      break;
                   }
                   if ((InterpTime > LineTime[j]) && (InterpTime < LineTime[j + 1]))//EC1時(shí)間在 POS 線上 2 個(gè)時(shí)間之間,計(jì)算EC1時(shí)間對(duì)應(yīng)坐標(biāo)
                   {
                      InterpEast = LineEast[j] + (LineEast[j + 1] - LineEast[j]) / (LineTime[j + 1] - LineTime[j]) * (InterpTime - LineTime[j]);//水深點(diǎn)東坐標(biāo)
                      InterpNorth = LineNorth[j] + (LineNorth[j + 1] - LineNorth[j]) / (LineTime[j + 1] - LineTime[j]) * (InterpTime - LineTime[j]);//水深點(diǎn)北坐標(biāo)
                      break;
                   }
               }
           }
     // 嘗試如下代碼更耗時(shí),相當(dāng)于增加了循環(huán),盡管代碼簡(jiǎn)潔
      找到大于目標(biāo)的第一個(gè)元素的索引
     //int indexGreaterThan = Array.FindIndex(LineTime, n => n > InterpTime);
      找到等于于target的第一個(gè)元素的索引
     //int indexEqualThan = Array.FindIndex(LineTime, n => n == InterpTime);
      找到小于target的第一個(gè)元素的索引
     //int indexLessThan = Array.FindLastIndex(LineTime, n => n < InterpTime);
     //if (indexEqualThan >= 0)
     //{
     //    InterpEast = LineEast[indexEqualThan];
     //    InterpNorth = LineNorth[indexEqualThan];
     //}

     //if (indexLessThan >= 0 && indexGreaterThan >= 0)
     //{
     //    InterpEast = LineEast[indexLessThan] + (LineEast[indexGreaterThan] - LineEast[indexLessThan]) / (LineTime[indexGreaterThan] - LineTime[indexLessThan]) * (InterpTime - LineTime[indexLessThan]);//水深點(diǎn)東坐標(biāo)
     //    InterpNorth = LineNorth[indexLessThan] + (LineNorth[indexGreaterThan] - LineNorth[indexLessThan]) / (LineTime[indexGreaterThan] - LineTime[indexLessThan]) * (InterpTime - LineTime[indexLessThan]);//水深點(diǎn)北坐標(biāo)
     //}
       }
}

3.3 、采用并行循環(huán)

為了提高到更快的運(yùn)算速度和效率,采用并行循環(huán),優(yōu)化代碼結(jié)構(gòu),調(diào)用類(lèi)封裝的坐標(biāo)計(jì)算循環(huán)為 InterpCoord 函數(shù)。插補(bǔ) EC1 采集點(diǎn)的坐標(biāo)和 FIX 采集的水深,整體循環(huán)減小到 377 毫秒左右。

   Parallel.For(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];
   });

四、雙循環(huán)更好的辦法

多線程處理不當(dāng)容易內(nèi)存溢出,線程間的錯(cuò)誤處理和同步問(wèn)題。需要嚴(yán)謹(jǐn)?shù)谋苊舛鄠€(gè)線程同時(shí)訪問(wèn)同一資源,否則導(dǎo)致沖突或錯(cuò)誤。‌存在內(nèi)存銷(xiāo)毀消耗,處理不當(dāng)反而浪費(fèi)時(shí)間,增加開(kāi)銷(xiāo)。其它方法還在探索中…

以上就是加快C#雙循環(huán)的速度效率的方法的詳細(xì)內(nèi)容,更多關(guān)于C#雙循環(huán)速度的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論