1. 程式人生 > >對於qsort和sort使用效率的詳細對比

對於qsort和sort使用效率的詳細對比

測試環境 VS2017
思路:用qsort與sort分別對有n個隨機數的陣列進行m次排序。
平臺:x64

sort:

標頭檔案: algorithm
函式原型:

template< class RandomIt >
  void sort( RandomIt first, RandomIt last );
  
template< class RandomIt, class Compare >
  void sort( RandomIt first, RandomIt last, Compare comp );

  
sort作為STL庫的成員函式,肯定是本著庫通用的目的,採用模板超程式設計實現,可對STL庫提供的大部分(?不知道是不是所有,目前僅排序過vector,string(list有自己的sort))容器進行排序(我猜應該是隻能針對連續地址的資料進行排序)。
引數值:要排序的起始迭代器位置,尾後迭代器位置,比較函式(可選)。預設是升序排列。

如果想要降序排列,可以:1,過載要排序的元素型別的<操作符
2,傳遞一個比較函式,如果第一個引數小於第二個該函式,返回true(升序)。
如果第一個引數大於第二個該函式,返回true(降序)。

比較函式的原型為:

bool cmp(const Type1 &a, const Type2 &b);
cmp 函式的返回值 描述
true elem1將被排在elem2前面
false 對elem1與elem2的次序不做改變

qsort:

標頭檔案: cstdlib
函式原型:

void qsort(void *base , int nelem ,int width , int (*fcmp)(const void *,const void *));

qsort作為C語言標準庫函式,可對連續地址儲存的變數進行排序,沒有預設排序方式,必須傳入比較函式,並且相對於sort的操作符過載的方式改變排序規則的策略來講,qsort只能通過改變比較函式的方式進行排序,策略單一。
引數值:排序的起始地址,排序的元素長度,要排序元素在記憶體中的佔位值(即sizeof),比較函式指標

比較函式的原型為:

int compare( (void *) & elem1, (void *) & elem2 );
compare 函式的返回值 描述
“< 0” elem1將被排在elem2前面
“0” elem1 等於 elem2
“> 0” elem1 將被排在elem2後面

**這裡需要注意的是,不同於qsort的比較函式,fcmp返回的是一個int值型別,而不是單純的true和false
如果比較函式寫成**

int comp(const void*a, const void*b)
{
    return *(int*)a>*(int*)b;//當a<b時就會返回false,也就是0,那麼會被qsort認為二者相等,最終導致排序錯誤

    /*正確寫法*/
    //return *(int*)a-*(int*)b;
}

使用例項及效率比較:

最開始我是看到這篇文章中對於二者的比較:http://blog.csdn.net/pku_zzy/article/details/51462417
作者得出的結論是,qsort比sort的效率高,一般情況下前者的用時是後者的三分之一,所以他推薦在一般情況下我們應該選擇qsort而不是sort。

對這一結論我有疑惑:qsort只是實現了快速排序,而STL庫中的sort實現了快排,堆排,歸併排等多種排序,並且針對資料量的不同做了諸多優化,如果最終效率不及qsort,那麼STL實現sort的意義在哪兒?

而且,我本人對STL庫是抱著非常敬畏和尊敬的感情的,我不相信STL庫的開發者們會做出這樣沒有意義的事情,並且還把它加到C++的標準庫中。

帶著如上疑問,我決定自己動手寫一個測試,來看看qsort和sort的排序效率到底是怎樣的。

以下為測試程式碼:

#include <cstdio>
#include <windows.h>
#include <cstdlib>
#include<vector>
#include<iostream>
#include<algorithm>
#include<ctime>
using namespace std;

#define MAX_SIZE 1000
#define N_TIMES 10000

int inline comp(const void*a, const void*b)//當返回值大於0時,認為a>b,如果此時與實際情況相符,則按升序排列,相反則按降序排列
                                            //返回值等於0,認為a=b,返回值小於0認為a<b;
                                            //特別需要注意的是,這裡不能返回諸如(*(int*)a)  > (*(int*)b)的形式,因為這樣只會返回0和1,而0是被認為二者是相等的
{
    return *(int*)a - *(int*)b;
    //return 0;
}

inline bool comp_sort(int a, int b)//當返回值為true時,認為a>b,否則,認為a<=b
{
    return a < b;
    //return false;
}


int main(void)
{
    vector<vector<int> > collect_qsort;
    vector<vector<int> > collect_sort;
    vector<int> temp(MAX_SIZE);
    srand((unsigned)time(NULL));//置隨機種子
    for (int i = 0; i < MAX_SIZE; i++)//將temp填滿隨機數
    {
        temp[i] = rand();
    }
    for (int i = 0; i < N_TIMES; i++)//兩個測試樣例都填充N_TIMES次temp
                                    //這裡兩種排序的測試樣例是完全相同的,都是對temp進行N_TIMES次排序
    {
        collect_qsort.push_back(temp);
        collect_sort.push_back(temp);
    }
    //下面是計時操作一些相關變數的初始化
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nBeginTime;
    LARGE_INTEGER nEndTime;
    double time;

    QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
    QueryPerformanceCounter(&nBeginTime);//獲取系統當前計數器計數值
    /***************************用qsort對樣例進行N_TIMES次排序***************************************/
    for(int i=0;i<N_TIMES;i++)
        qsort(&collect_qsort[i][0], MAX_SIZE,sizeof(collect_qsort[i][0]),comp);
    /****************************************************************************************/
    QueryPerformanceCounter(&nEndTime);//獲取系統當前計數器計數值
    time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;//兩次獲取的計數器計數值的差值除以系統時鐘頻率即可得到時間,精確到微秒
    printf("cost of Qsort time(%dint data*%d):%f\n", MAX_SIZE,N_TIMES,time);


    QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
    QueryPerformanceCounter(&nBeginTime);//獲取系統當前計數器計數值
    /***************************用qsort對樣例進行N_TIMES次排序***************************************/
    for (int i = 0; i<N_TIMES; i++)
        sort(collect_sort[i].begin(), collect_sort[i].end(), comp_sort);
    /****************************************************************************************/
    QueryPerformanceCounter(&nEndTime);//獲取系統當前計數器計數值
    time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;//兩次獲取的計數器計數值的差值除以系統時鐘頻率即可得到時間,精確到微秒
    printf("cost of sort time(%dint data*%d):%f\n", MAX_SIZE, N_TIMES, time);


    system("Pause");
    return 0;
}

在該項測試中,我們選取了1000個int型的隨機資料進行排列,分別用sort與qsort對其進行1e4次排列。

在debug模式下,我發現確實如上面那篇部落格的作者所言,sort的用時幾乎是qsort的三倍甚至更多
這裡寫圖片描述

但是IDE在debug模式下對程式碼的監視是要耗費資源的,越大規模的演算法,在debug模式下對其監視所花費的資源也就越多,龐大如VS這樣的IDE,debug所消耗的資源更甚。

因此同樣的程式碼我又在release模式下跑了一遍:
這裡寫圖片描述

可以看到,在release模式下,兩種排序的時間都大幅降低了,並且sort的用時是明確小於qsort的用時的。
所以,到這裡我可以肯定,上面那篇部落格的作者的測試就是在debug和release的選擇上出了問題。

為了得出一般性的結論,我們選擇不同的輸入規模,來看一看二者的效率是否會發生逆轉
修改測試程式碼中的MAX_SIZE 與 N_TIMES(N_TIMES的作用僅僅是在小規模輸入下增加對同樣的資料的排序次數,以消除計時誤差帶來的影響,不影響sort與qsort的效率的排名結果)
我們來看一看不同規模的輸入下,二者的相對錶現如何:

輸入規模(MAX_SIZE ) 排序次數(N_TIMES) qsort用時 sort用時 qsort:sort
10 10000000 1.915217s 0.853853s 2.24:1
100 1000000 3.634150s 2.172662s 1.67:1
1000 100000 8.398367s 5.571055s 1.51:1
10000 10000 10.812114s 8.111797s 1.33:1
100000 1000 13.201909s 10.600798s 1.25:1
1000000 100 11.275247s 9.838172s 1.15:1
10000000 10 11.255788s 9.987730s 1.13:1
100000000 1 10.782703s 9.452227s 1.14:1

由上表我們可以看出,從10到1e8(即1億)的資料量級的比較中,雖然隨著資料量級的增長,qsort的效率越來越接近sort的效率,但是qsort:sort的值始終是大於1的。 因此不難得出結論,一般情況下而言,sort的效率始終是高於qsort的效率的

因為int是內建型別,我想可能會有影響,所以又定義了一個自定義型別Point

class Point
{
public:
    int x;
    int y;
};

以Point點到座標原點的距離作為排序的依據:
程式碼實現如下

#include <cstdio>
#include <windows.h>
#include <cstdlib>
#include<vector>
#include<iostream>
#include<algorithm>
#include<ctime>
using namespace std;

class Point
{
public:
    int x;
    int y;
};

int inline comp(const void*a, const void*b)//當返回值大於0時,認為a>b,如果此時與實際情況相符,則按升序排列,相反則按降序排列
                                           //返回值等於0,認為a=b,返回值小於0認為a<b;
                                           //特別需要注意的是,這裡不能返回諸如(*(Point*)a)  > (*(Point*)b)的形式,因為這樣只會返回0和1,而0是被認為二者相等的
{
    return ((Point*)a)->x* ((Point*)a)->x + ((Point*)a)->y* ((Point*)a)->y - ((Point*)b)->x* ((Point*)b)->x - ((Point*)b)->y* ((Point*)b)->y;
}

inline bool comp_sort(Point &a, Point &b)//當返回值為true時,認為a>b,否則,認為a<=b
{
    return a.x*a.x + a.y*a.y < b.x*b.x + b.y*b.y;
}




#define MAX_SIZE 10000
#define N_TIMES 10000
int main(void)
{
    vector<vector<Point> > collect_qsort;
    vector<vector<Point> > collect_sort;
    vector<Point> temp(MAX_SIZE);
    srand((unsigned)time(NULL));//置隨機種子
    for (int i = 0; i < MAX_SIZE; i++)//將temp填滿隨機數
    {
        temp[i].x = rand() % 100;
        temp[i].y = rand() % 100;
    }
    for (int i = 0; i < N_TIMES; i++)//兩個測試樣例都填充N_TIMES次temp
                                     //這裡兩種排序的測試樣例是完全相同的,都是對temp進行N_TIMES次排序
    {
        collect_qsort.push_back(temp);
        collect_sort.push_back(temp);
    }
    //下面是計時操作一些相關變數的初始化
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nBeginTime;
    LARGE_INTEGER nEndTime;
    double time;

    QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
    QueryPerformanceCounter(&nBeginTime);//獲取系統當前計數器計數值
    /***************************用qsort對樣例進行N_TIMES次排序***************************************/
    for (int i = 0; i<N_TIMES; i++)
        qsort(&collect_qsort[i][0], MAX_SIZE, sizeof(collect_qsort[i][0]), comp);
    /****************************************************************************************/
    QueryPerformanceCounter(&nEndTime);//獲取系統當前計數器計數值
    time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;//兩次獲取的計數器計數值的差值除以系統時鐘頻率即可得到時間,精確到微秒
    printf("cost of Qsort time(%dPoint data*%d):%f\n", MAX_SIZE, N_TIMES, time);


    QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
    QueryPerformanceCounter(&nBeginTime);//獲取系統當前計數器計數值
    /***************************用qsort對樣例進行N_TIMES次排序***************************************/
    for (int i = 0; i<N_TIMES; i++)
        sort(collect_sort[i].begin(), collect_sort[i].end(), comp_sort);
    /****************************************************************************************/
    QueryPerformanceCounter(&nEndTime);//獲取系統當前計數器計數值
    time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;//兩次獲取的計數器計數值的差值除以系統時鐘頻率即可得到時間,精確到微秒

    printf("cost of sort time(%dPoint data*%d):%f\n", MAX_SIZE, N_TIMES, time);



    system("Pause");
    return 0;
}

最終結論沒有改變:
這裡寫圖片描述

**

因此,任何情況下,我都推薦你使用sort。

**

相關推薦

對於qsortsort使用效率詳細對比

測試環境 VS2017 思路:用qsort與sort分別對有n個隨機數的陣列進行m次排序。 平臺:x64 sort: 標頭檔案: algorithm 函式原型: template< class RandomIt >   void so

常見資料結構演算法效率對比

1. 資料結構部分 資料結構中常用的操作的效率表   通用資料結構 查詢  插入   刪除 遍歷 

C語言和C++自帶排序比較(qsortsort)

我們先來看看C語言的,qsort函式,下面是具體的實現。 #include <stdio.h> #include <stdlib.h> /*const 讓它不能被修改,以只讀型別被用 如果函式引數是任意型別指標,就用void* */ /*qsor

qsortsort的區別

qsort 基本快速排序的方法,每次把陣列分成兩分和中間的一個劃分值,而對於有多個重複值的陣列來說,基本排序的效率較低。整合在C語言庫函式裡面的的qsort函式,使用 三 路劃分的方法解決這個問題。所謂三路劃分,是指把陣列劃分成小於劃分值,等於劃分值和大於劃分值的三

VC介面庫BCGControlBarXtreme Toolkit詳細對比評測

筆者最近專案中正好要用到VC介面庫,於是在網上搜索一下,發現以前大家分享了不少MFC相關的介面庫,但大多都已經多年不更新,甚至連地址都不復存在。最後筆者鎖定了兩款"存活"至今的VC介面庫: 和。它們的共同特點是都支援微軟Office和Visual Studio風格,這裡就跟大家分享一下筆者的一些試用體驗。

JavaWeb學習篇之----容器Response詳解(有關response setCharacterEncodingsetContentType)詳細對比說明

今天在來看一下Response容器的相關知識,其實這篇blog早就應該編寫了,只是最近有點忙,所以被中斷了。下面我們就來看一下Response容器的相關知識吧。Response和我們即將在後面說到的Request容器是一一對應的,他是web容器在使用者每次請求服務端的

雲端計算廠商國內海外價格詳細對比

隨著雲端計算的不斷髮展,各個廠家的競爭也越來越激烈,但是綜合資料分析還是“品牌”廠商可以提供更加可靠、可信、穩定的雲服務,廠商、配置、地域等因素不同,雲產品價格不同,此外雲端計算廠商各有所長,下面我主要從IAAS層面針對阿里雲、騰訊雲、微軟雲、AWS和華為雲國內、海外做一個詳細全面的價

SSHSSM——詳細對比總結

        當下流行的兩種企業開發MVC開源框架,是我們Java程式猿必備知識能力。MVC,即模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將業務

redis使用管道普通模式下執行效率對比

1 普通模式 $redis = new Redis;     $redis->connect("127.0.0.1","6379");     $redis->auth("123456");     for($i=0;$i<10000;$i++){   

LINUX下兩種tar打包(.bz2)(.gz)壓縮效率時間對比試驗

試驗檔案大小:204M,檔名:xx.dat 壓縮 1. tar czvf test.tar.gz xx.dat 耗時20秒,打包後大小:123M      2.tar cjvf test.tar.bz2 xx.dat  耗時82秒,打包後大小:133M 解壓 1. tar

常用資料結構演算法操作效率對比總結

歡迎關注我新搭建的部落格:[http://www.itcodai.com/](http://www.itcodai.com/)         前面介紹了經典的資料結構和演算法,這一節我們對這些資料結構和演算法做一個總結,具體細節,請參見各個章節的詳細介紹,這裡我們用表

qsort()函式sort()函式

2. STL 中 sort 函式用法簡介 做 ACM 題的時候,排序是一種經常要用到的操作。如果每次都自己寫個冒泡之類的O(n^2) 排序,不但程式容易超時,而且浪費寶貴的比賽時間,還很有可能寫錯。 STL 裡面有個 sort 函式,可以直接對陣列排序,複雜度為n*log2(n) 。 使用這個函式,需要包

關於php strtr str_replace 效率的問題

abcde 優化 bcd php 7 abcdefg 5.6 nginx 網上 環境 在網上看了一些php優化的指南,裏面提到:使用strtr 函數 比 str_replace快4倍。 本著探索的精神動手驗證。 代碼 $string = ‘abcdefg‘; set_

Android Dalvik虛擬機ART虛擬機對比

x文件 開始 過程 優秀 clas 編譯 apk 但是 好的 1.概述  Android4.4以上開始使用ART虛擬機,在此之前我們一直使用的Dalvik虛擬機,那麽為什麽Google突然換了Android運行的虛擬機呢?答案只有一個:ART虛擬機更優秀。 2.Dalvik

mapreduce shuffle sort 詳解

改變 struct 堆內存 傳輸 工具 默認 臨時 arc 快速排序 MapReduce 框架的核心步驟主要分兩部分:Map 和Reduce。當你向MapReduce 框架提交一個計算作業時,它會首先把計算作業拆分成若幹個Map 任務,然後分配到不同的節點上去執

Python基礎課:定義一個函數,可以對序列逆序的輸出(對於列表元組可以不用考慮嵌套的情況)

int 情況 type spa list bsp pri not log 1 15 def fun(arg): 2 16 if type(arg) is not tuple 3 17 and type(arg) is not str 4 18

MySQL與Oracle的語法區別詳細對比

變量 into lpad while循環 獲得 var 無符號 這樣的 ims Oracle和mysql的一些簡單命令對比 1) SQL> select to_char(sysdate,‘yyyy-mm-dd‘) from dual;   SQL> select

shufflesort分析

理解 不同 http 寫入 mapr 中一 重復 進入 ons MapReduce中的Shuffle和Sort分析 MapReduce 是現今一個非常流行的分布式計算框架,它被設計用於並行計算海量數據。第一個提出該技術框架的是Google 公司,而Google 的靈感則來自

MyBatisHibernate的優缺點對比

hiberna 編寫 完全 都是 關聯 生成 模式 相對 臟數據 Hibernate的優點: 1、hibernate是全自動,hibernate完全可以通過對象關系模型實現對數據庫的操作,擁有完整的JavaBean對象與數據庫的映射結構來自動生成sql。 2、功能強大,數據

【持久化框架】Mybatis與Hibernate的詳細對比

很大的 效率 myba 今天 http 目的 ping pin 增刪 作為一位優秀的程序員,只知道一種ORM框架是遠遠不夠的。在開發項目之前,架構的技術選型對於項目是否成功起到至關重要的作用。我們不僅要了解同類型框架的原理以及技術實現,還要深入的理解各自的優缺點,以便我們能