2020华为软挑热身赛 个人总结

热身赛讲实话并没有花太多的时间,队友做了更多的工作,本人只对读取数据的模块进行了比较多的处理。

比赛的名头是机器学习相关,实际上操作的时候发现也不完全是。可以使用机器学习的技巧,不过我们的队伍主要使用优化的知识,维持原来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均为不同的函数。

这里还需要调查更多的资料。。。。

更多细节

比较琐碎,直接按点罗列:

  • ARM上处理整型数据比浮点型要快
  • switch-caseif-else要快
  • memcpy拷贝字符数组,比for循环挨个赋值要更快
  • 输出到文件中的换行符用endl非常慢,用'\n'
  • mmap可能会比fread更快,这个存疑,因为fread本身读取整个数据到字符串的时间也很短,主要时间都在将字符串处理成为浮点数。总之,mmap可以参考这篇帖子
  • main调用的函数栈有大小上线,差不多是10001000个浮点数,所以当前数据80001000的量,只能使用global变量,并且全局变量似乎运行效率要比局部变量更高。
  • 数据写入要用fwrite,我们这里都没做优化…

你可能感兴趣的:(其他)