热身赛讲实话并没有花太多的时间,队友做了更多的工作,本人只对读取数据的模块进行了比较多的处理。
比赛的名头是机器学习相关,实际上操作的时候发现也不完全是。可以使用机器学习的技巧,不过我们的队伍主要使用优化的知识,维持原来LR回归的框架,改换了迭代算法。而比赛的主要评分指标并非准确率而是运行速度,对于我们这种搞高性能计算,机器学习算法是小白的同学反而是一件好事。
个人最终成绩0.66s,排名150+,队伍里有同学最后两天继续调试把时间压到0.3s了,不过不管咋样都进不了前50的0.04s的量级。初赛复赛尽量努力吧,能走多远走多远。
不过来参赛了总要学点新知识。这里首先总结一下自己使用的技巧,然后总结一下大佬们的代码和我的不同。
和大佬们的思路一样,使用cin
或者sin
等等读取数据,要比按字符串读入,再自己解析成浮点数要慢很多。而数据也比较工整,大概形式如下:
0.213,0.142,0.123
-0.122,0.412,0.412
用逗号分隔,并且都只有4位的浮点数,但是会有负号,不过数据量是8000行每行1000个数据。读取代码如下:
void loadTrainData(string trainFile)
{
FILE *fp;
fp = fopen(trainFile.c_str(),"rb");
int len = fread(buf,1,MAXS,fp);
buf[len] = '\0';
double position = 1.0;
int symbol = 0;
int point = 0;
int i,j;
double line[WIDTH+5];
i = 0;
j = 0;
for (char *p=buf; *p && p-buf
就是将数据一次性按照字符串全部读入,然后再慢慢拼接成浮点数。
这一优化能够比官方给提供的读取程序快一个量级,0.1s这个量级下就能将训练数据全部读取。
其实就是指针在索引的时候会有额外的运算量。而测试数据的数据规模为20000*1000,并且官方要求必须全部读取。另外队友发现官方测试数据并没有负值,所以程序可以大大简化。
void loadTestData2(string testFile)
{
FILE *fp;
fp = fopen(testFile.c_str(),"rb");
int len = fread(buf,1,MAXS,fp);
buf[len] = '\0';
double position = 1.0;
int point = 0;
int i,j;
double line[WIDTH+5];
double *q = line;
double *pf = &features_test[0][0];
line[0] = 0.0;
char *p = buf;
*q = *p - '0';
*q += 0.1*(*(p+2)-'0')+0.01*(*(p+3)-'0')+0.001*(*(p+4)-'0');
p+=5;
q++;
*q = 0.0;
while (*p && p-buf
主要是数组的索引全部改成了指针,并且进行了一点点循环展开,把if分支大幅度减少。
这个优化可以让读取速度再快一倍多,读取全部的20000*1000测试数据需要0.09s左右。
不过队友提醒我其实不用写if,直接用for就行,因为每行的数据个数已知。emmmm…有道理,不过最后我也没实现。
队友进行了多进程的改写,不过程序排版比较…反正没读完…这里参考大佬们的多进程代码进行总结。
这里参考这篇帖子。核心就是:
//四个子线程从四等分处向后解析数据
std::thread thread0(readAndCal0, buf);
std::thread thread1(readAndCal1, len, buf);
std::thread thread2(readAndCal2, len, buf);
std::thread thread3(readAndCal3, len, buf);
//等待四个子线程解析完
thread0.join();
thread1.join();
thread2.join();
thread3.join();
其中thread0.join()
是阻塞进程直到线程执行完毕,相当于并行的barrier
。而std::thread thread0
相当于创建了一个对象thread0
,后面跟的则是构造函数的输入值,这里对应的是要执行的函数,和这个函数的输入参数。这里大概是thread0,thread1,thread2,thread3
均为不同的函数。
这里还需要调查更多的资料。。。。
比较琐碎,直接按点罗列:
switch-case
比if-else
要快memcpy
拷贝字符数组,比for
循环挨个赋值要更快endl
非常慢,用'\n'
mmap
可能会比fread
更快,这个存疑,因为fread
本身读取整个数据到字符串的时间也很短,主要时间都在将字符串处理成为浮点数。总之,mmap
可以参考这篇帖子main
调用的函数栈有大小上线,差不多是10001000个浮点数,所以当前数据80001000的量,只能使用global
变量,并且全局变量似乎运行效率要比局部变量更高。fwrite
,我们这里都没做优化…