好久沒寫博客了,不是太忙,是太懶了。。。
最近都在重構公司項目上的代碼,然後就發現有部分函數的運行方式可以優化。這些函數的運行的運行方式都是先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;
}
需要注意的是,靜態局部變量在多線程中會引起多線程競爭,所以在多線程中使用上述編碼轉換函數時需小心,避免多個線程同時運行同一個編碼轉換函數,要是無法避免,可以選擇加鎖。
上面就是我本篇博客的全部內容了。本人限於能力,上述代碼中難免有錯誤的地方,若讀者發現上述代碼的錯誤,請於評論區中指出,本人看到之後會立即修改的,謝謝。