在C++中,同一功能的函数用不同方法实现,所需要的时间开销可能会有差异。比较不同实现方式的时间开销,有利于更好地达到目的。
strcpy中不同字符操作方式
现以实现C语言中的strcpy函数为例,比较字符行数据的单个移动和整块移动、用指针方式操纵字符和用数组方式操纵字符之间的时间差异。本实验环境为win10的VS2022平台,程序运行选择的解决方案平台为x64,具体程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// 方式1:以指针方式实现strcpy char* i_strcpy_1(char* dst, const char* src) { if (dst == nullptr || src == nullptr) { return dst; } char* ptr = (char*)dst; while (*ptr++ = *src++) { } return dst; } // 方式2:以数组方式实现strcpy char* i_strcpy_2(char* dst, const char* src) { if (dst == nullptr || src == nullptr) { return dst; } int idx = 0; //long long idx = 0; while (dst[idx] = src[idx]) { ++idx; } return dst; } // 方式3:使用memcpy函数进行整块拷贝 char* i_strcpy_3(char* dst, const char* src) { if (dst == nullptr || src == nullptr) { return dst; } memcpy(dst, src, strlen(src) + 1); return dst; } |
C++统计时间开销的方法
chrono
是C++时间操作库的头文件。在C++中统计时间开销可使用如下方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <chrono> std::chrono::steady_clock::time_point start; // 记录开始时间 std::chrono::steady_clock::time_point end; // 记录结束时间 std::chrono::nanoseconds dt; // 时间差 start = std::chrono::steady_clock::now(); // 开始计时 // 事件过程 /* ... */ end = std::chrono::steady_clock::now(); // 结束计时 // 统计时长 dt = end - start; // 转换为秒 (double)dt.count() / (1000 * 1000 * 1000) |
不同字符操纵方式的时间差异
经过测试,在C++中使用memcpy函数进行整块字符操纵,其效率远高于对单个字符依次移动。
在对字符依次单个操纵的方式中,根据条件的不同,时间开销也略有不同。
下面为使用分文件编程的测试程序,分别对三个函数进行一亿次调用,统计其时间开销:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include "i_strcpy.h" using namespace std; int main() { char str[101] = { 0 }; int cnt = 100000000; // 100 000 000 std::chrono::steady_clock::time_point start, end; std::chrono::nanoseconds dt; // i_strcpy_1()耗时 start = chrono::steady_clock::now(); for (int i = 0; i < cnt; ++i) { i_strcpy_1(str, "Hello, ikaros. Welcome to the world! I'm Ling."); } end = chrono::steady_clock::now(); dt = end - start; cout << "函数i_strcpy_1耗时(单位:秒):" << (double)dt.count() / (1000 * 1000 * 1000) << endl; // i_strcpy_2()耗时 start = chrono::steady_clock::now(); for (int i = 0; i < cnt; ++i) { i_strcpy_2(str, "Hello, ikaros. Welcome to the world! I'm Ling."); } end = chrono::steady_clock::now(); dt = end - start; cout << "函数i_strcpy_2耗时(单位:秒):" << (double)dt.count() / (1000 * 1000 * 1000) << endl; // i_strcpy_3()耗时 start = chrono::steady_clock::now(); for (int i = 0; i < cnt; ++i) { i_strcpy_3(str, "Hello, ikaros. Welcome to the world! I'm Ling."); } end = chrono::steady_clock::now(); dt = end - start; cout << "函数i_strcpy_3耗时(单位:秒):" << (double)dt.count() / (1000 * 1000 * 1000) << endl; return 0; } |
经过多次测试,时间开销均为使用memcpy方式时间最少,使用数组方式其次,使用指针方式时间最大。
当不使用分文件方式时,使用memcpy方式时间依旧最少。而数组和指针方式,由于数组下标的差异,有所差别。
在64位平台下,C++指针变量占用的内存空间为8字节,int
类型的变量占用4字节,long long
类型的变量占用8字节。
当数组使用int类型的下标时,数组方式所占用的时间均少于指针方式,其中一次测试结果如下图所示。
当数组使用long long类型的下标时,数组方式所占用的时间略多于指针方式,其中一次测试结果如下图所示。
结论
在C++中操纵字符时,为提高效率,应尽量使用memcpy
和memmove
等内置函数操纵整块字符串。
对单个字符依次操纵时,指针和数组方式各有优势,使用数组更简单。
留言