前言
弄了一台 Dell XPS 9570, 4K屏。終於滿足了我在高分屏下玩 Linux 桌面的願望。我已經給這台電腦安裝了最新的 Ubuntu 20.10,並進行了適當的美化。昨天,又折騰了一晚上 Vim,主要是配置了 YouCompleteMe 和 vim-airline,所以迫不及待地想寫寫代碼找找手感。那就測試一下我的電腦能運行多快,計時的精度有多高吧,順便看看,通過我自己手擼的快速排序算法,程序又能提速到多快。
Linux 下的計時及其精度
以前用的計時函數是 gettimeofday(),其精度可以達到毫秒。有一天我在翻看《Unix環境高級編程》的時候,發現里面提到,gettimeofday() 早就廢棄不用了,現在推薦使用 clock_gettime() 函數,精度可以達到 1 納秒。聽起來都好誘人。
下面寫個小程序測試一下,就是連續兩次調用 clock_gettime() 函數,然后打印時間間隔,看我的電腦記一次時,最快能達到多少納秒。程序如下:
#include <iostream>
#include <iomanip>
#include <sys/time.h>
using namespace std;
void print_timer(struct timespec start, struct timespec end){
long timer = (end.tv_sec - start.tv_sec)*1000000000 + end.tv_nsec - start.tv_nsec;
cout << setw(3) << setfill('0') << timer/1000000000 << ",";
cout << setw(3) << setfill('0') << (timer/1000000)%1000 << ",";
cout << setw(3) << setfill('0') << (timer/1000)%1000 << ",";
cout << setw(3) << setfill('0') << timer % 1000 << " ns" << endl;
}
int main(){
struct timespec start;
struct timespec end;
clock_gettime(CLOCK_REALTIME, &start);
clock_gettime(CLOCK_REALTIME, &end);
print_timer(start, end);
return 0;
}
本來寫這篇文章就是想找找 Vim 的手感,所以必須來一張 Vim 的圖片,如下:
運行結果如下:
從上面的結果可以看出,我的電腦計時的精度可以達到 348ns,真的好厲害。以后,我的所有需要測試程序運行時間的,都使用上面這樣的模板代碼。
測試對長度為 1000000 的數組進行排序的時間:1.冒泡排序
冒泡排序的代碼我就不列出了,因為是在是太簡單了,就是兩重循環,其時間復雜度為 \(O(n^2)\) ,那么,我們測出的時間是多少呢?
2645,822,408,482 ns
兩千多秒,也就是四十多分鍾,實在是太慢了。
測試對長度為 1000000 的數組進行排序的時間:2.快速排序
當然,上面程序的慢是每一個算法書上都會提到的,所以我們需要使用更快的算法。我這里使用的是快速排序,其時間復雜度為 \(O(n\log{n})\) 。算法是我手擼的,如下:
#include <iostream>
#include <iomanip>
#include <sys/time.h>
using namespace std;
const int ARR_LEN = 1000000;
template <typename T>
void init_arr(T* a, int len){
for(int i=0; i<len; i++){
a[i] = (T)rand();
}
}
template <typename T>
void print_arr(T* a, int len){
if(len<10){
for(int i=0; i<len; i++){
cout << a[i] << " ";
}
cout << endl;
}else{
for(int i=0; i<5; i++){
cout << a[i] << " ";
}
cout << "... ";
for(int i=len-5; i<len; i++){
cout << a[i] << " ";
}
cout << endl;
}
}
template <typename T>
void quick_sort(T* a, int len){
if(len<2)return;
if(len == 2){
if(a[0] > a[1]){
T temp = a[0];
a[0] = a[1];
a[1] = temp;
}
}
int i=0, left=0, right=len-1;
while(left < right){
while(a[right] > a[i] && left < right){
right--;
}
if(left<right){
T temp = a[right];
a[right] = a[i];
a[i] = temp;
i = right;
left ++;
}
while(a[left] < a[i] && left<right){
left++;
}
if(left<right){
T temp = a[left];
a[left] = a[i];
a[i] = temp;
i = left;
right--;
}
}
quick_sort(a, i);
quick_sort(a+i+1, len-i-1);
}
void print_timer(struct timespec start, struct timespec end){
long timer = (end.tv_sec - start.tv_sec)*1000000000 + end.tv_nsec - start.tv_nsec;
cout << setw(3) << setfill('0') << timer/1000000000 << ",";
cout << setw(3) << setfill('0') << (timer/1000000)%1000 << ",";
cout << setw(3) << setfill('0') << (timer/1000)%1000 << ",";
cout << setw(3) << setfill('0') << timer % 1000 << " ns" << endl;
}
int main(){
int* a = (int*)malloc(sizeof(int)*ARR_LEN);
init_arr(a, ARR_LEN);
print_arr(a, ARR_LEN);
struct timespec start;
struct timespec end;
clock_gettime(CLOCK_REALTIME, &start);
quick_sort(a, ARR_LEN);
clock_gettime(CLOCK_REALTIME, &end);
print_arr(a, ARR_LEN);
print_timer(start, end);
return 0;
}
編譯,運行。它的時間是多少呢?
000,123,209,970 ns
也就是 123ms,比冒泡排序足足提速兩萬一千五百多倍。我的天哪!看來算法還是非常重要的呀。
測試對長度為 1000000 的數組進行排序的時間:3.使用多線程的快速排序
這是最快的速度了嗎?顯然不是,我的電腦 CPU 可是 6 核 12 線程的。不充分利用多核優勢怎么對得起自己。我上面的快速排序算法使用的是遞歸,那么使用 OpenMP 就沒戲了(主要是我自己水平太菜,沒找到遞歸算法的 OpenMP 寫法)。那么只好自己寫 thread 了,好在是自從 C++ 11 之后,寫 thread 也很簡單。我的方法就是在 quick_sort() 函數中判斷一下待排序的數組的長度,如果待排序的數組的長度大於總數組長度的 1/8,就在新創建的線程里遞歸調用 quick_sort(),否則就直接遞歸調用 quick_sort()。如下圖:
編譯,運行。它的時間是多少呢?
000,040,038,803 ns
速度再次提升三倍,終於使時間達到了 40ms 的級別。這可能就是我的電腦和我本人的水平所能達到的最快速度吧。
下面是運行截圖:
總結
電腦很 NB,Linux 的計時器也很 NB,算法很 NB,配置了 YouCompleteMe 和 vim-airline 的 Vim 也很 NB。
版權申明
該隨筆由京山游俠在2021年05月16日發布於博客園,引用請注明出處,轉載或出版請聯系博主。QQ郵箱:1841079@qq.com