優化C++ utf8,gbk,unicode編碼間的轉換函數

好久沒寫博客了,不是太忙,是太懶了。。。

最近都在重構公司項目上的代碼,然後就發現有部分函數的運行方式可以優化。這些函數的運行的運行方式都是先new出一堆內存,使用,最後delete掉。我就想,可不可以通過靜態局部變量來重複使用已經new了的動態內存,以達到優化代碼的運行的目的?然後我就用visual studio 2017進行了測試,下面是我的測試代碼:

#include <random>
#include <new>
#include <chrono>
#include <iostream>
#include <vector>
#include <string>

int main()
{
	std::default_random_engine dre;
	std::uniform_int_distribution<unsigned> uid;
	std::vector<unsigned> vec_data_length;
	std::chrono::milliseconds used_time;
	std::string test_string = "";
	std::chrono::steady_clock::time_point begin_time_point = std::chrono::steady_clock::now();

	for (size_t loop_times = 0; loop_times < 2000 * 2000; loop_times++)
	{
		vec_data_length.push_back(uid(dre) % 10000);
	}

	begin_time_point = std::chrono::steady_clock::now();
	for (auto data_length : vec_data_length)
	{
		if (data_length > 0)
		{
			test_string.assign(data_length, 'z');
			char* p_data = new char[data_length]();
			memcpy(p_data, test_string.c_str(), data_length);
			delete[] p_data;
		}
	}
	used_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - begin_time_point);
	std::cout << used_time.count() << std::endl; // release模式下大約2604ms

	// 通過動態內存的再次使用,可以節省大約52%的時間開銷,需要注意邊界條件data_length + 1
	begin_time_point = std::chrono::steady_clock::now();
	for (auto data_length : vec_data_length)
	{
		static char* sp_data = nullptr;
		static size_t s_data_length = 0;

		if (data_length > 0)
		{
			test_string.assign(data_length, 'z');
			data_length += 1;
			if (s_data_length < data_length)
			{
				delete[] sp_data;
				s_data_length = data_length;
				sp_data = new char[s_data_length]();
				memset(sp_data, 0, s_data_length * sizeof(char)); // 注意長度
			}
			else
			{
				memset(sp_data, 0, data_length * sizeof(char)); // 注意長度
			}

			memcpy(sp_data, test_string.c_str(), test_string.size());
		}
	}
	used_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - begin_time_point);
	std::cout << used_time.count() << std::endl; // release模式下大約1205毫秒

	return 0;
}

可以看到,在我的電腦上,release模式,沒有優化的代碼運行大約需要2604毫秒,優化後的代碼運行大約需要1205毫秒,優化的程度還是很大的。

在這裏就有人想,沒有delete不是會造成內存泄漏?由於靜態局部變量的析構是在main函數運行完畢之後執行的,也就是說程序退出之後纔有內存泄漏,但是windows會自動回收內存,也就是不存在內存泄漏了。

有了上面的優化測試,那麼我就想優化前面編寫的《 C++ UTF-8,wstring,string 之間的轉換》文章中的代碼。下面是優化後的代碼:

std::wstring Utf8ToUnicode(const std::string &strUTF8)
{
	int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);
	if (len == 0)
	{
		return L"";
	}

	static wchar_t *spRes = NULL;
	static int sDataLen = 0;

	if (sDataLen < len)
	{
		delete[] spRes;
		sDataLen = len;
		spRes = new wchar_t[sDataLen];
		if (spRes == NULL)
		{
			sDataLen = 0;
			return L"";
		}
		memset(spRes, 0, sDataLen * sizeof(wchar_t));
	}
	else
	{
		memset(spRes, 0, len * sizeof(wchar_t));
	}

	MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, spRes, len);
	spRes[len - 1] = L'\0';
	std::wstring result = spRes;

	return result;
}

std::string UnicodeToUtf8(const std::wstring &strUnicode)
{
	int len = WideCharToMultiByte(CP_UTF8, 0, strUnicode.c_str(), -1, NULL, 0, NULL, NULL);
	if (len == 0)
	{
		return "";
	}

	static char *spRes = NULL;
	static int sDataLen = 0;

	if (sDataLen < len)
	{
		delete[] spRes;
		sDataLen = len;
		spRes = new char[sDataLen];
		if (spRes == NULL)
		{
			sDataLen = 0;
			return "";
		}

		memset(spRes, 0, sDataLen * sizeof(char));
	}
	else
	{
		memset(spRes, 0, len * sizeof(char));
	}

	WideCharToMultiByte(CP_UTF8, 0, strUnicode.c_str(), -1, spRes, len, NULL, NULL);
	spRes[len - 1] = '\0';
	std::string result = spRes;

	return result;
}

// gbk轉unicode
std::wstring StringToWString(const std::string &str)
{
	int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
	if (len == 0)
	{
		return L"";
	}

	static wchar_t *spRes = NULL;
	static int sDataLen = 0;

	if (sDataLen < len)
	{
		delete[] spRes;
		sDataLen = len;
		spRes = new wchar_t[sDataLen];
		if (spRes == NULL)
		{
			sDataLen = 0;
			return L"";
		}
		memset(spRes, 0, sDataLen * sizeof(wchar_t));
	}
	else
	{
		memset(spRes, 0, len * sizeof(wchar_t));
	}
	
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, spRes, len);
	spRes[len - 1] = L'\0';
	std::wstring result = spRes;
	
	return result;
}

// unicode轉gbk
std::string WStringToString(const std::wstring &wstr)
{
	int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
	if (len == 0)
	{
		return "";
	}
	
	static char *spRes = NULL;
	static int sDataLen = 0;
	
	if (sDataLen < len)
	{
		delete[] spRes;
		sDataLen = len;
		spRes = new char[sDataLen];
		if (spRes == NULL)
		{
			sDataLen = 0;
			return "";
		}
		
		memset(spRes, 0, sDataLen * sizeof(char));
	}
	else
	{
		memset(spRes, 0, len * sizeof(char));
	}
	
	WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, spRes, len, NULL, NULL);
	spRes[len - 1] = '\0';
	std::string result = spRes;
	
	return result;
}

需要注意的是,靜態局部變量在多線程中會引起多線程競爭,所以在多線程中使用上述編碼轉換函數時需小心,避免多個線程同時運行同一個編碼轉換函數,要是無法避免,可以選擇加鎖。

上面就是我本篇博客的全部內容了。本人限於能力,上述代碼中難免有錯誤的地方,若讀者發現上述代碼的錯誤,請於評論區中指出,本人看到之後會立即修改的,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章