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<len; p++)
   {   if (*p==',')
       {   if(symbol==1)
                line[i] = -line[i];
            i++;
            point = 0;
            position = 1.0;
            symbol = 0;
            line[i] = 0.0;
        }
        else if (*p=='\n')
        {   if(symbol==1)
                 line[i] = -line[i];

            //save data to global array         
            labels[j] = (int)line[WIDTH];
            for(i=0;i<WIDTH;i++)
               features[j][i] = line[i];

            j++;
            if(j==SIZE) 
                break;
            i = 0;
            point = 0;
            position = 1.0;
            symbol = 0;
            line[i] = 0.0;
        }
        else if (*p=='.')
        { point = 1;}
        else if (*p=='-')
        { symbol = 1;}
        else
        {  if (point == 0)
                line[i] = line[i]*10 + *p - '0';
            else
            {   position = position*0.1;
                line[i] += position*(*p-'0');
            }
        }

    }
} 

就是将数据一次性按照字符串全部读入,然后再慢慢拼接成浮点数。

这一优化能够比官方给提供的读取程序快一个量级,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<len)
   { if(*p==',')
     { //1st
       *q = *(p+1) - '0';
       *q += 0.1*(*(p+3)-'0')+0.01*(*(p+4)-'0')+0.001*(*(p+5)-'0');
       p+=6;

       q++;
       *q = 0.0;

       //2nd
       *q = *(p+1) - '0';
       *q += 0.1*(*(p+3)-'0')+0.01*(*(p+4)-'0')+0.001*(*(p+5)-'0');
       p+=6;

       q++;
       *q = 0.0;

       //3rd
       *q = *(p+1) - '0';
       *q += 0.1*(*(p+3)-'0')+0.01*(*(p+4)-'0')+0.001*(*(p+5)-'0');
       p+=6;

       q++;
       *q = 0.0;

     }
     else if (*p=='\n')
     { q = line;
       for (i=0;i<WIDTH;i++)
	   *pf++ = *q++;

       q = line;
       *q = 0.0;

       *q = *(p+1) - '0';
       *q += 0.1*(*(p+3)-'0')+0.01*(*(p+4)-'0')+0.001*(*(p+5)-'0');
       p+=6;

       q++;
       *q = 0.0;
     }
   }
}

主要是数组的索引全部改成了指针,并且进行了一点点循环展开,把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,我们这里都没做优化…
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值