学习构建之法(1):写个微信抢票

历时 5 周,历经 Alpha、Beta 两次迭代,终于把软件工程这门课的两人项目做完交付了(?)

粗略计算,与我同组的刘斌同学在这个项目上花费了百余个小时的时间。我本人则少一些,未作确切统计。从截止日期临近时另一专业课 13 / (~60) 的出勤率、去校外咖啡馆刷夜的人数、汇报展示现场主讲教师建群发红包“熬夜辛苦,中午吃好点”的情景,可见这项作业的工作量之大(?)

这个项目最主要完成的是测试环节,至于需求分析、软件设计、团队流程等则涉及较少。

根据课程要求,撰写一篇博客。择其重点,总结一下主要的收获。

  • 设计实现
    • 体会到 Web 应用开发时前后端分离的好处
    • 再次体验了给助教华榕大帝改 bug
  • 团队合作
  • 软件测试
    • 认识了 django 测试框架、浏览器自动化工具 selenium 和 PhantomJS
    • 知道了编写单元测试的基本方法
    • 了解了编写功能测试的基本方法
    • 了解了评价测试的主要指标——覆盖率
  • 性能调优,相关博文:django 性能调优手记
    • 性能测试
      • 认识了性能测试工具 JMeter
      • 了解了评价性能的主要指标——吞吐量、错误率、响应时间等
    • 部署和运维
      • 认识了众多 Linux 系统参数、uWSGI 参数、nginx 参数
      • 尝试了基于 docker 的自动化部署
    • 性能优化
      • 体会到若干因素对性能的影响

此系列还有另一篇博文:学习构建之法(2):课程总结

我以为自己发现了 clang 的一个 bug……

摘要:完成体系结构课程作业时遇到 gcc 和 clang 的行为不一样。确切地说是 clang -O1 及以上会得到意外的结果。以为自己搞了个大新闻,结果发现是自己写了 ub(未定义行为,undefined behavior)。

代码如下。

#include <stdio.h>
#include <limits.h>
int test(int x, int n)
{
  int y = (1 << (n - 1)) - 1;
  int le = (x <= y);
  printf("%d, %d, %d\n", x, y, le);
  return 0;
}

int main(){
  int n = sizeof(int) * 8;
  test(INT_MIN, n);
  return 0;
}

gcc 版本:gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
输出的结果是:

$ gcc bar.c -O1 -o g && ./g
-2147483648, 2147483647, 1

clang 版本:Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
输出的结果是:

$ clang bar.c -O1 -o c && ./c
-2147483648, 2147483647, 0

 

好奇怪啊,明明一个正数、一个负数,怎么 clang -O1 就负数更大了呢???

是不是 clang 编译优化的 bug?

——我也以为自己搞了个大新闻。仔细研究一番便发现,这里并不能怪编译器:1 << 31 已经发生了溢出;即使得到了负值 INT_MIN,在其上再减去 1,再次发生溢出。对于有符号整数运算溢出,尽管很常见的做法是截断取模,但实际上标准并没有作出规定。因此,从这一行起,编译器做什么都是符合标准的( ̄(工) ̄)

ub 很可能直接被编译器优化掉[1]。此处就是这样。可以从汇编码看出,开启编译优化时,clang 把 le 参数直接优化掉了,大意如此:

printf("%d, %d, %d\n", x, y, le); //clang -O0
printf("%d, %d, %d\n", x, y, 0);  //clang -O1

完。