`
qiezi
  • 浏览: 492739 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C++/D/Python性能比较续

阅读更多
周末抽空做了点小测试,根据http://blog.vckbase.com/jzhang/archive/2006/03/28/18807.html中m网友修改的算法,python版本中读取所有行以后就做一个排序,再去除重复项。这个算法在我的机器上执行时间是1735ms左右,属于python版本中最快的一个。

D版本暂还没想到有更优化的做法,D在处理以char[]作key的关联数组时,判断方法是先判断hash,如果hash相等,则继续做字符串判断。它执行时间是1120ms左右。

以D版本为基础,自己写了一个C++的Email类:

<!---->class Email
{
private:
        
string mail;
        size_t hash;
        friend 
bool operator < (const Email& lhs, const Email& rhs);
public:
        Email (
const char* mail_)
                : mail(mail_), hash(my_hash(mail_))
        {
        }
};

bool operator < (const Email& lhs, const Email& rhs)
{
        
if (lhs.hash == rhs.hash)
                
return lhs.mail < rhs.mail;
        
return lhs.hash < rhs.hash;
}

把它插入set并判断是否有重复。

这个程序由于string的大量拷贝,以及大量内存分配,执行时间相当长,在我的机器上是5s左右。D和python版本由于对象拷贝成本较低,加上都有内存分配策略,自然有一些优势。

退而求其次,既然hash冲突的几率较低,试试只保存hash:

<!---->class Email
{
private:
        size_t hash;
        friend 
bool operator < (const Email& lhs, const Email& rhs);
public:
        Email (
const char* mail_)
                : hash(my_hash(mail_))
        {
        }
};

bool operator < (const Email& lhs, const Email& rhs)
{
        
return lhs.hash < rhs.hash;
}

这次测试就比较快了,耗时仅1020ms左右,比D版本还要快,当然它不是完善的版本。

考虑到构造成本,于是改为只用一个set<int>来保存hash值再测试,这次耗时是930ms。

实际上可以做一个改进的C++版本,一次性读入文件的全部内容到一个大缓冲区,把所有的\n字符修改为\0,用一个动态数组保存缓冲区的所有字符串指针,hash值也须计算并保存到数组。再用D的索引方式,先hash比较,再字符串比较,效率应该也不低。

实现了一个:
<!---->#include <iostream>
#include 
<string>
#include 
<set>
#include 
<fstream>
#include 
<iterator>
#include 
<sys/time.h>
using namespace std;


size_t my_hash (
const char* str)
{
        size_t ret 
= 0;
        
while (*str)
                ret 
= 11 * ret + *str++;
        
return ret;
}

class Email
{
private:
        size_t hash;
        
const char* mail;
        friend 
bool operator < (const Email& lhs, const Email& rhs);
public:
        Email (
const char* mail_)
                : hash(my_hash(mail_)), mail(mail_)
        {
        }
};

bool operator < (const Email& lhs, const Email& rhs)
{
        
if (lhs.hash == rhs.hash)
                
return strcmp(lhs.mail, rhs.mail) < 0;
        
return lhs.hash < rhs.hash;
}

int main(int argc, char** argv)
{
        
if (argc < 3)
        {
                cout 
<< "Wrong arguments" << endl;
                
return 1;
        }

        FILE
* fin = fopen(argv[1], "r");
        
if (!fin)
        {
                cout 
<< "Invalid input file" << endl;
                
return 2;
        }
        FILE
* fout = fopen(argv[2], "w");
        
if (!fout)
        {
                fclose(fin);
                cout 
<< "Invalid output file" << endl;
                
return 3;
        }

        timeval start, end;

        
const int BUF_SIZE = 20 * 1024 * 1024;
        
char* buffer = new char[BUF_SIZE];
        memset(buffer, 
0, BUF_SIZE);

        gettimeofday(
&start, 0);
        
set<Email> emails;

        size_t read 
= fread (buffer, BUF_SIZE, 1, fin);
        
char* begin = buffer;
        
char* current = buffer;

        
while (*current != '\0')
        {
                
if (*current == '\n')
                {
                        
*current = '\0';
                        
if (emails.insert(begin).second){
                                fputs(begin, fout);
                                fwrite(
"\n"11, fout);
                        }
                        begin 
= current + 1;
                }
                
++ current;
        }

        fclose(fout);
        fclose(fin);

        gettimeofday(&end, 0);

        printf(
"Time used: %d ms\n", ((end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000));

        delete[] buffer;
        
return 0;
}

memset不是必须的,不过我不知道如何获取读入的大小。fread读取后,如果读到EOF,则返回值为0。所以我这里用memset先初始化内存,但不把它计入耗时。new操作也没计入,因为其它语言比如python、D在启动时都由运行时做了类似工作。

这个程序在我的机器上耗时为1350ms左右。我想可慢在set上,对象拷贝?内存分配?

做了几个优化版本,没多大提高。


重新测试了下:
A、python(m网友版本):
lijie t # python test.py
1689.0411377
lijie t # python test.py
1711.40599251
lijie t # python test.py
1699.63312149
lijie t # python test.py
1712.00013161
lijie t # python test.py
1713.8838768

B、D版本:
lijie t # ./testd email.txt email-new.txt
1091
lijie t # ./testd email.txt email-new.txt
1070
lijie t # ./testd email.txt email-new.txt
1062
lijie t # ./testd email.txt email-new.txt
1062
lijie t # ./testd email.txt email-new.txt
1096

C、C++只比较hash,set<Email>版本:
lijie t # ./test3 email.txt email-new.txt
Time used: 981 ms
lijie t # ./test3 email.txt email-new.txt
Time used: 1000 ms
lijie t # ./test3 email.txt email-new.txt
Time used: 980 ms
lijie t # ./test3 email.txt email-new.txt
Time used: 986 ms
lijie t # ./test3 email.txt email-new.txt
Time used: 987 ms

D、C++只比较hash,set<int>版本:
lijie t # ./test4 email.txt email-new.txt
Time used: 951 ms
lijie t # ./test4 email.txt email-new.txt
Time used: 953 ms
lijie t # ./test4 email.txt email-new.txt
Time used: 947 ms
lijie t # ./test4 email.txt email-new.txt
Time used: 950 ms
lijie t # ./test4 email.txt email-new.txt
Time used: 962 ms

E、C++大缓冲区,比较hash和字符串,set<Email>版本:
lijie t # ./test5 email.txt email-new.txt
Time used: 1375 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1359 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1369 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1378 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1396 ms

F、C++大缓冲区,比较字符串版本:
lijie t # ./test6 email.txt email-new.txt
Time used: 1168 ms
lijie t # ./test6 email.txt email-new.txt
Time used: 1169 ms
lijie t # ./test6 email.txt email-new.txt
Time used: 1171 ms
lijie t # ./test6 email.txt email-new.txt
Time used: 1179 ms
lijie t # ./test6 email.txt email-new.txt
Time used: 1169 ms

从C、E和F来看,对象拷贝成本是比较高的,E版本仅仅比C版本多了个const char*成员变量,hash值比较散,很少会真的执行到strcmp。保持E版本对象结构不变,把operator <里面的实现改为C版本,测试结果如下:
lijie t # ./test5 email.txt email-new.txt
Time used: 1355 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1360 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1348 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1353 ms
lijie t # ./test5 email.txt email-new.txt
Time used: 1379 ms

效率只提高了一点点,这个版本仅仅比C版本多了个成员变量拷贝,竟然慢了这么多。说明Email对象的2个成员变量拷贝成本的确很高。

F版本相比之下反而效率很不错,主要原因是email数据不够复杂,仅通过前几位就可以比较出结果。如果每行数据比较长,而且很多行要到后几个字符才能比较出来,肯定就不那么快了。

hash值的计算虽然执行了一系列乘法,不过还是相当迅速。

D语言版本执行了hash值和字符串比较,是比较完善的,效率很不错。C++相应版本看来要提高set的效率才能达到。


jzhang的第一个python版本在我的机器上执行如下:
lijie t # python test2.py
3122.9569912 ms
lijie t # python test2.py
3209.42997932 ms
lijie t # python test2.py
3141.47305489 ms
lijie t # python test2.py
3129.57286835 ms
lijie t # python test2.py
3196.03514671 ms

我做了点修改,执行速度提高了一些:
<!---->#remove duplicated email address from file
import datetime
from time import time
if __name__ == "__main__":
 start 
= time()
 hashtable 
= {}
 f 
= file("email.txt","r")
 f2 
= file("email_new.txt","w")
 
for line in f.xreadlines():
  
if not hashtable.has_key(line):
   hashtable[line] 
= 1
   f2.write(line)
 f.close()
 f2.close()
 print (time() 
- start) * 1000"ms"
在我的机器上执行结果如下:
lijie t # python test1.py
2239.22801018 ms
lijie t # python test1.py
2301.00703239 ms
lijie t # python test1.py
2282.06086159 ms
lijie t # python test1.py
2296.57006264 ms
lijie t # python test1.py
2281.25810623 ms

不过还是没有m网友的效率高。


在F版本的基础上,借鉴m网友的做法,实现一个G版本:

G、排序并去除重复元素,比较hash和字符串版本:
<!---->#include <iostream>
#include 
<string>
#include 
<fstream>
#include 
<iterator>
#include 
<sys/time.h>
#include 
<vector>
using namespace std;


size_t my_hash (
const char* str)
{
        size_t ret 
= 0;
        
while (*str)
                ret 
= 11 * ret + *str++;
        
return ret;
}

class Email
{
private:
        size_t hash;
        
const char* mail;
        friend 
bool operator < (const Email& lhs, const Email& rhs);
public:
        Email (
const char* mail_)
                : hash(my_hash(mail_)), mail(mail_)
        {
        }

        
bool operator == (const Email& rhs)
        {
                
if (hash == rhs.hash)
                        
return strcmp(mail, rhs.mail) == 0;
                
return false;
        }

        
const char* getEmail()const
        {
                
return mail;
        }
};

bool operator < (const Email& lhs, const Email& rhs)
{
        
if (lhs.hash == rhs.hash)
                
return strcmp(lhs.mail, rhs.mail) < 0;
        
return lhs.hash < rhs.hash;
}

int main(int argc, char** argv)
{
        
if (argc < 3)
        {
                cout 
<< "Wrong arguments" << endl;
                
return 1;
        }

        FILE
* fin = fopen(argv[1], "r");
        
if (!fin)
        {
                cout 
<< "Invalid input file" << endl;
                
return 2;
        }
        FILE
* fout = fopen(argv[2], "w");
        
if (!fout)
        {
                fclose(fin);
                cout 
<< "Invalid output file" << endl;
                
return 3;
        }

        timeval start, end;

        
const int BUF_SIZE = 20 * 1024 * 1024;
        
char* buffer = new char[BUF_SIZE];
        memset(buffer, 
0, BUF_SIZE);

        gettimeofday(
&start, 0);
        vector
<Email> emails;

        size_t read 
= fread (buffer, BUF_SIZE, 1, fin);
        
char* begin = buffer;
        
char* current = buffer;

        
while (*current != '\0')
        {
                
if (*current == '\n')
                {
                        
*current = '\0';
                        emails.push_back(begin);
                        begin 
= current + 1;
                }
                
++ current;
        }
        fclose(fin);
        sort(emails.begin(), emails.end());
        emails.erase (unique( emails.begin(), emails.end() ), emails.end());

        
for (vector<Email>::const_iterator iter = emails.begin();
             iter 
!= emails.end();
             iter 
++)
        {
                fputs((
*iter).getEmail(), fout);
                fwrite(
"\n"11, fout);
        }

        fclose(fout);

        gettimeofday(
&end, 0);

        printf(
"Time used: %d ms\n", ((end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000));

        delete[] buffer;

        
return 0;
}

在我的机器上执行如下:
lijie t # ./test7 email.txt email-new.txt
Time used: 676 ms
lijie t # ./test7 email.txt email-new.txt
Time used: 675 ms
lijie t # ./test7 email.txt email-new.txt
Time used: 671 ms
lijie t # ./test7 email.txt email-new.txt
Time used: 669 ms
lijie t # ./test7 email.txt email-new.txt
Time used: 673 ms

比F版本快了2倍,也快过了其它所有版本。不过由于数据是vector保存的,在数据大量重复的情况下,性能可能会有较大的降低。

把operator<和operator==的实现改为strcmp比较,执行结果如下:
lijie t # ./test8 email.txt email-new.txt
Time used: 1275 ms
lijie t # ./test8 email.txt email-new.txt
Time used: 1267 ms
lijie t # ./test8 email.txt email-new.txt
Time used: 1297 ms
lijie t # ./test8 email.txt email-new.txt
Time used: 1296 ms
lijie t # ./test8 email.txt email-new.txt
Time used: 1271 ms


修改了下,增加了计时,修正了fread使用错误。

<!---->#include <iostream>
#include 
<string>
#include 
<fstream>
#include 
<iterator>
#include 
<vector>
using namespace std;

#ifdef _WIN32
# include 
<windows.h>
#else // _WIN32
# include 
<sys/time.h>
#endif // _WIN32


size_t my_hash (
const char* str)
{
        size_t ret 
= 0;
        
while (*str)
                ret 
= 11 * ret + *str++;
        
return ret;
}

class Email
{
private:
        size_t hash;
        
const char* mail;
        friend 
bool operator < (const Email& lhs, const Email& rhs);
public:
        Email (
const char* mail_)
                : hash(my_hash(mail_)), mail(mail_)
        {
        }

        
bool operator == (const Email& rhs)
        {
                
if (hash == rhs.hash)
                        
return strcmp(mail, rhs.mail) == 0;
                
return false;
        }

        
const char* getEmail()const
        {
                
return mail;
        }
};

bool operator < (const Email& lhs, const Email& rhs)
{
        
if (lhs.hash == rhs.hash)
                
return strcmp(lhs.mail, rhs.mail) < 0;
        
return lhs.hash < rhs.hash;
}

#ifndef _WIN32
class Timer
{
        timeval begin, end;
public:
        
void start () {gettimeofday(&begin, 0);}
        
void stop () {gettimeofday(&end, 0);}
        size_t milliseconds () 
const {
                
return (end.tv_sec - begin.tv_sec) * 1000 + (end.tv_usec - begin.tv_usec) / 1000;
        }
};
#else // _WIN32
class Timer
{
        DWORD begin, end;
public:
        
void start () {begin = GetTickCount();}
        
void stop () {end = GetTickCount();}
        size_t milliseconds () 
const {
                
return end - begin;
        }
};
#endif // _WIN32


int main(int argc, char
分享到:
评论

相关推荐

    ca源码java-CardRaytracerBenchmark:这是简短的C++/Java/C#/Python基准测试。根据PaulHeckb

    估算性能指标。 得出结论 针对用户的简短说明: 下载并安装Java,Python,CSharp,Digital Mars D,GoLang,JavaScript环境。 运行“运行基准” 阅读报告。 开发者 梅顿 鲁斯兰(基隆) MasterZiv(masterziv) dr-...

    C++标准库介绍.pdf

    标准c库大全:C++标准库介绍 疯狂代码 http://CrazyCoder.cn/ ĵ:http:/CrazyCoder.cn/VC/Article12860.html  标准库中提供了C基本设施虽然C标准库随着C标准折腾了许多年直到标准出台才正式定型但是在标准库实 现上...

    langs-performance:C ++,Python,Perl,PHP,Java,NodeJS,Go,Ruby,Rust,Swift和D性能基准

    语言性能C ++,Python,Perl,PHP,Java,NodeJS,Go,Ruby,Rust,Swift和D性能基准测试博客文章: 2016年: : 2016年: : 2010-2012年: : 这里的基准测试并没有尽力而为,因为它们从一个方面展示了语言的性能,...

    高洛峰 memcache for window 和linux版软件及教程

    客户端使用各种语言去编写 PHP/java/c/c++/perl/python/ruby等 libevent 三、为什么要在WEB中使用Memcache 基于libevent事件 Linux下 安装libevent时 ./configure –with-libevent=/usr Make ...

    VNTrader.rar

    全新架构,性能再次升级,python的便捷,C++性能加持,比老版本更好用,性能提升300%以上,全新系统命名未VNTrader,属于VNPY官方发布的重点全新架构的产品。 VNTrader的Python和底层C++代码全部开源, 这个是一个...

    D 语言 2.0 编程参考手册(上中下)

    它的重点在于整合了 C 和 C++ 的强大及高性能,同时又具有像现 代语言 Ruby 和 Python 一样的程序员生产率。对于其它像质量保证、文档、管理、移植性 和可靠性等这些需求,它也给予了特别的关注。 D 语言需要静态...

    redis2加强.doc

    特性 1〉速度快,数据放在内存中,官方给出的读写性能 10 万/S,与机器性能也有关 a,数据放内存中是速度快的主要原因 b,C 语言实现,与操作系统距离近 ...9〉客户端语言多:java php python c c++ nodejs 等

    完全不科学的基准:几种编程语言(JavaScript,Kotlin,Rust,Swift,Nim,Python,Go,Haskell,D,C ++,Java,C#,Object Pascal,Ada,Lua,Ruby)的幼稚性能比较

    我们以几种经典(C ++,Java,Python)和炒作(JavaScript,Kotlin,Swift,Rust)编程语言实现了 ,并测试了它们在Linux,Mac OS和Windows(均在不同硬件上运行)的性能,因此不应在平台之间比较结果)。...

    java8集合源码-concept-queen:不同语言的皇后算法

    我也有兴趣比较性能。 目前该项目涵盖 Python、Perl、Ruby、PHP、C、C++、Java、Nodejs、FreeBasic、FreePascal、D、CSharp(Mono)、Scala、Groovy、Go、Kotlin、Lua、Dart 和 julia。 谁赢? 这不是 100% 清楚; ...

    MySQL中文参考手册.chm

    &lt;br/&gt;7.4.4 逻辑运算 &lt;br/&gt;7.4.5 比较运算符 &lt;br/&gt;7.4.6 字符串比较函数 &lt;br/&gt;7.4.7 类型转换运算符 &lt;br/&gt;7.4.8 控制流函数 &lt;br/&gt;7.4.9 数学函数 &lt;br/&gt;7.4.10 字符串函数 &lt;br/&gt;7.4.11 日期和时间函数 &lt;br/&gt;7.4.12 ...

    数据结构与算法分析:C语言描述高清版 带源码

    数据结构与算法分析:C语言描述 高清版 PDF 带源码 非c++本书是《Data Structures and Algorithm Analysis in C》一书第2版的简体中译本。原书曾被评为20世纪顶尖的30部计算机著作之一,作者Mark Allen Weiss在数据...

    IONIC 功能全演示

    - c++编译环境[MSVStudio 免费版](https://www.visualstudio.com/downloads/download-visual-studio-vs#d-express-windows-desktop).。注意根据studio不同版本指定 --msvs_version=2013 选项 - 安装项目开发依赖包,...

    汉诺塔java源码-LangBenchmarks:多种编程语言的性能基准

    用多种编程语言编写了两个性能测试,并比较了它们的计算速度。 循环测试 该测试评估了带有 while 子句的变量迭代的简单循环中的代码速度。 def cycle ( n ): i = 0 while i &lt; n : i += 1 河内测试 测试扩展递归以...

    zapata:RESTful API的C ++开发支持库,以Emiliano Zapata命名

    Zapata是用于C ++的RESTful API开发框架,基于Ømq通信库构建,旨在实现高性能和高可伸缩性。 它遵循C ++ 14和C ++ 17个标准和编码样式,包括lambda函数,promisses和assync编程。 它提供了由RESTful模式封装和抽象...

    Imath:Imath是计算机图形学的2D和3D向量,矩阵和数学运算的C ++库

    Imath还包括适用于所有类型和功能的可选python绑定,包括向量和矩阵数组的优化实现。 项目任务 Imath项目的目标是简单,易用,正确性和可验证性,性能以及采用的广度。 Imath并非旨在成为全面的线性代数或数值分析...

    labor:作业服务,看起来很轻巧

    如果未找到python27_d.lib,请复制python27.lib并将其重命名为同一目录中的python27_d.lib /msvcbuild有一个VS2013解决方案文件,您可以在depends/win32_lib找到所有第三方库。 但是我建议通过CMake来增加劳动力。...

    fib:Github顶级语言的性能基准

    其他:Crystal,Rust,Swift,Mono,Elixir,Perl,R,Julia,D,Nim,Pascal,Fortran,Cython,Pony,OCaml,Lisp,Haskell,Erlang,Escript,Dart,Clojure,Scheme,Lua,Python3, Perl,Perl6,Bash,Emoji ...

    RED HAT LINUX 6大全

    9.6.2 /etc/rc.d httpd脚本 168 9.7 配置文件清单 170 9.8 小结 185 第10章 Internet新闻 186 10.1 Linux与新闻组 186 10.1.1 新闻供给点如何工作 187 10.1.2 推/拉新闻 187 10.1.3 下载新闻组的可选方法 187 10.2 ...

Global site tag (gtag.js) - Google Analytics