程式設計雜談——std::vector與List<T>的效能比較
昨天在比較完C++中std::vector的兩個方法的效能差異並留下記錄後——程式設計雜談——使用emplace_back取代push_back,今日嘗試在C#中測試對應功能的效能。
C#中對應std::vector的資料結構為List
std::vector - List
std::list - LinkedList
std::map - Dictionary<K, V>
std::set - HashSet
std::multimap - Dictionary<K, List>
C#的測試程式碼如下:
using System; using System.Collections.Generic; using System.Diagnostics; namespace ConsoleApp { class Item { public string Name { get; set; } } class Program { static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); var count = 100000; var l = new List<Item>(); for (int i = 0; i < count; i++) { l.Add(new Item { Name = "Test" }); } Console.WriteLine(sw.ElapsedMilliseconds / 1000.0); } } }
程式執行結果約為0.077(該值每次會發生少許變化)
而C++的測試程式碼的結果約是0.207。這就有違於我們一般的認知了,畢竟通常都覺得C++的效能要優於C#。
#include <iostream> #include <vector> #include <chrono> class Item { public: Item(std::string name):name(name){} private: std::string name; }; int main() { std::vector<Item> v; int count = 100000; v.reserve(count); clock_t begin_time = clock(); for (auto i = 0; i < count; i++) { v.emplace_back("Test"); } std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl; }
原來這裡犯了個錯誤,如果要進行基準測試的話,必須要在Release模式下進行。
當改成Release模式後,C++的程式碼執行時間變成了0.003,而C#也下降到了0.061左右。
不過在上述C#程式碼中,缺少一點優化,var l = new List<Item>();
沒有預設容量值,如果改成var l = new List<Item>(count);
,執行時間進一步下降至0.050左右。
然而C#程式碼還可以繼續優化,將Item類改成結構體後,結果變成了0.006。
struct Item
{
public string Name { get; set; }
}
如果把C++程式碼中也同樣改成結構體,則幾乎沒有獲得什麼優化。
struct Item
{
public:
Item(std::string name):name(name){}
private:
std::string name;
};
最後將測試資料量從10W加至1000W後,C++程式碼的執行時間約是0.286,而C#的約為0.627。同樣是2倍左右的差距。
值得注意的是,上述的C#程式碼是在.NET Core 3.0基礎上測試,如果改成.NET Framwork 4.8,執行時間會降為0.536左右。由此可見,.NET Core應該還留有不少可以優化的地方,希望其在效能方面上能夠進一步改善。