wayne
发表于 2018-1-5 11:07:27
一发不可收拾,我用下面的代码能够说明时间统计的稳定性以及误差的传递,大家可以看看
#include <iostream>
#include <chrono>
void hello()
{
for( int i = 0,j=0; i < 1e8; ++i ) j++;
}
int main()
{
for(int k=0;k<10;++k){
auto t1 = std::chrono::high_resolution_clock::now();
hello();
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();
std::cout << duration/1000. <<"ms"<< std::endl;
}
return 0;
}
运行结果是
222.795ms
223.371ms
218.557ms
221.014ms
217.021ms
221.605ms
221.48ms
219.964ms
221.761ms
220.376ms
可见,波动范围是2ms, 统计时间的最后一个不可靠度的位置是1ms, 加大被测试的函数里的循环次数1e8变成10e9, 误差发生了传递,或者说被放大了,最后一个不可靠的位置是10ms,所以,真正严谨的benchmarking还得统计一下耗时的稳定性,即你的统计数据的有效位是哪一位.
2236.23ms
2242.03ms
2209.1ms
2220.01ms
2210.53ms
2212.43ms
2205.2ms
2214.93ms
2221.3ms
2239.92ms
liangbch
发表于 2018-1-5 18:11:17
对于CPU,虽然我们可以从文档中得到每条指令的时钟周期(如http://www.agner.org/optimize/instruction_tables.pdf),但是,你如果做性能测试时会发现,这些数据最多只有一点儿参考价值而已,总的运行时间和指令的时钟周期的累加值差别很大。你永远无法计算出实际运算时间。甚至每次的运行时间都不一样。这不仅和线程切换,中断等外部因素有关,还包括大量的未知因素。过去,我的性能测试总是测试多次(如5次或者7次),然后取最快的那个结果(纵然这样,结果依然是不稳定的,波动范围在10%以内,已经是非常好的了)。但是,为了公平起见(如Maple多次测试的时间差别极大),我改为多次测试的均值。
你如果运行一下GMP源码build出来的那个speed 程序,你会发现,每次得到的结果都不一样。在通常情况下,最大值,最小值在平均值+-%5上下波动。如“speed -C -E -s 1000mpn_mul_basecase”
liangbch
发表于 2018-1-5 18:22:10
mathe 发表于 2018-1-5 09:26
能够直接在C里面调用mathematica的内部接口,或者将你自己的算法编写成能够让mathematica调用的接口,然后 ...
不错的想法,需要研究下mathematica的外部接口怎么做。
wayne
发表于 2018-1-5 22:12:16
liangbch 发表于 2018-1-5 18:11
对于CPU,虽然我们可以从文档中得到每条指令的时钟周期(如http://www.agner.org/optimize/instruction_tab ...
nope, 个人认为这是很要命的。如果不事先声明数据的有效程度[方差,稳定性],我完全可以很安全的说 这些数据是没有什么实际的参考意义的。虽然从感情上,我相信你的人品,相信你有实力在某些算法细节上胜过Mathematica.
确实,网上很多各种benchmarking根本就没给出对应的测试数据的统计方差,我平时也不太当回事。不知怎么的,我竟然在这里较真起来了。莫非只有我一个人是这么对待数据的真实性吗?
=======================================================================
数据的方差大也不要紧,但就是不能没有。 方差大了,咱们还可以另外解释,我们可以说平均性能怎么样,最好性能怎么样,最坏性能怎么样。 是吧
wayne
发表于 2018-1-6 00:26:51
仿照例子,安装目录下【/usr/local/Wolfram/Mathematica/11.2/SystemFiles/Links/WSTP/DeveloperKit/Linux-x86-64】,写了一个测试C/C++测试程序。基本跑通需求。
存在的问题就是到底怎么设置时间的check point才算合理,不是特别的清楚。我先把代码贴出来,有兴趣的自己可以试试。暂时不打算继续较真下去了,到此为止。(反正Mathematica是输了还是赢了,跟我没任何关系, ^_^)
编译的命令如下:
g++ main.cxx -I. -L. -lWSTP64i4 -lm -lpthread -lrt -ldl -luuid -std=c++11
LD_LIBRARY_PATH=. ./a.out -linkname "math -wstp"
#include <iostream>
#include <chrono>
#include <cmath>
#include "wstp.h"
static WSENV ep = (WSENV)0;
static WSLINK lp = (WSLINK)0;
static void error( WSLINK lp)
{
if( WSError( lp)){
fprintf( stderr, "Error detected by WSTP: %s.\n",
WSErrorMessage(lp));
}else{
fprintf( stderr, "Error detected by this program.\n");
}
exit(3);
}
static void deinit( void)
{
if( ep) WSDeinitialize( ep);
}
static void closelink( void)
{
if( lp) WSClose( lp);
}
static void init_and_openlink( int argc, char* argv[])
{
int err;
ep =WSInitialize( (WSParametersPointer)0);
if( ep == (WSENV)0) exit(1);
atexit( deinit);
lp = WSOpenArgv( ep, argv, argv + argc, &err);
if(lp == (WSLINK)0) exit(2);
atexit( closelink);
}
static void read_and_print_expression( WSLINK lp)
{
const char *s;
int n;
int i, len;
double r;
static int indent;
switch( WSGetNext( lp)) {
case WSTKSYM:
std::cout<< __LINE__ <<std::endl;
WSGetSymbol( lp, &s);
printf( "%s ", s);
WSReleaseSymbol( lp, s);
break;
case WSTKSTR:
std::cout<< __LINE__ <<std::endl;
WSGetString( lp, &s);
printf( "\"%s\" ", s);
WSReleaseString( lp, s);
break;
case WSTKINT:
std::cout<< __LINE__ <<std::endl;
WSGetInteger( lp, &n);
printf( "%d ", n);
break;
case WSTKREAL:
// std::cout<< __LINE__ <<std::endl;
WSGetReal( lp, &r);
printf( "%g ", r);
break;
case WSTKFUNC:
std::cout<< __LINE__ <<std::endl;
indent += 3;
printf( "\n %*.*s", indent, indent, "");
if( WSGetArgCount( lp, &len) == 0){
error( lp);
}else{
read_and_print_expression( lp);
printf( "[");
for( i = 1; i <= len; ++i){
read_and_print_expression( lp);
if( i != len) printf( ", ");
}
printf( "]");
}
indent -= 3;
break;
case WSTKERROR:
std::cout<< __LINE__ <<std::endl;
default:
error( lp);
}
}
int main(int argc, char* argv[])
{
init_and_openlink( argc, argv);
int pkt,x,n;
std::cout<< "put x: "<<std::endl;
std::cin>>x;
std::cout<< "put n: "<<std::endl;
std::cin>>n;
std::string cmd = "N,"+std::to_string(n)+"]";
std::cout<< "Expression: "<<cmd<<std::endl;
// WSPutFunction( lp, "N", 2);
// WSPutFunction( lp, "Log", 1);
// WSPutInteger( lp, x);
// WSPutInteger( lp, n);
// WSEndPacket( lp);
WSPutFunction(lp, "ToExpression", 1);
WSPutString(lp, cmd.c_str());
WSEndPacket(lp);
while( (pkt = WSNextPacket(lp), pkt) && pkt != RETURNPKT) {
WSNewPacket( lp);
if (WSError( lp)) error( lp);
}
auto t1 = std::chrono::high_resolution_clock::now();
read_and_print_expression( lp);
auto t2 = std::chrono::high_resolution_clock::now();
std::cout<< "cost:"<<std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()/1000.<<"ms"<<std::endl;
WSPutFunction( lp, "Exit", 0L);
return 0;
}
zeroieme
发表于 2018-3-17 23:47:15
不知论文进度怎样了
liangbch
发表于 2018-3-20 09:55:53
这个事情暂时放下,也没有重启的时间表
无心人
发表于 2018-4-8 17:37:44
我提供个测试例子吧,统一使用一个很大的常数,比如10000内的质数乘到一起,结果减1,再平移小数点变成整数部分小于10的小数,预先存储成文件,然后读进程序来运算,然后再测试求时间,测试时候从读完这个小数后计时,这样就既能求的比最小测量误差大很多的时间了,又避免了可能的预先储存结果的问题
nyy
发表于 2022-11-15 14:17:40
我就好奇,干啥工作需要把对数算上万位?
liangbch
发表于 2022-12-13 11:14:56
nyy 发表于 2022-11-15 14:17
我就好奇,干啥工作需要把对数算上万位?
对技术的追求是无止尽的. 圆周率已经计算到几万亿位了,但就应用而言,几十位圆周率也足够了,不是吗?