你真的会写程序吗?
假如给定一个任务。任务内容为给一个Web应用写一段用户认证程序。你会怎么写呢?
给五分钟时间让你先想想!
......
怎么样?想得差不多了吧?
我也想好了!所以先看看我写的程序好吧:
然而,这看似合理的程序,其实是非常不合理的。
因为程序的第一行用掉两个方法调用(在绝大多数情况下,的确是两个)。而这两个调用其实却并不一定是必须的。在绝大多数情况下!
为什么?
因为这两句话检查的是HTTP客户端有没有传入username与password这两个参数。而在绝大多数情况下,这两个参数是会被传入的(至少在一个产品环境下)。在认证的第一行就检查这两个参数是否传入,就象机检时第一件事就查你有没有猪流感一样,那概率实在是太小了。因为基于目前的数据,显然患有人流感的乘客比起患有猪流感的乘客数量要大得多!更合理的流程显然是先查人流感,再查猪流感!据此,我对机场的安检效率的确有很大的意见。
类似的错误并不只在if-else句中出现。其它的分支语句如switch-case中也有这样的问题。特别是在switch语句中,大部分人喜欢按顺序写程序。比如:
基于条件发生频率的编程
所有的程序最终都将被译成机器码。不管是编译式程序还是解释型程序。最终的任务只能依赖硬件来完成。这就如我们的脑子虽然负责所有的思考活动,但却实际上不可能完成任何工作一样。因为任何工作最终都要依赖于人体这个硬件来完成(因为软件其实并不是真实存在的东西。它只是一个硬件的状态集。真正完成状态转换的其实并不是软件,甚至连硬件也不是,是电!---至少在电动力计算机中,是这样的)。
程序编译器或者解释器经常会对一些经常运行的代码进行一些优化。这种优化,基于的就是不同代码运行频率并不相同的原理。通过优化运行频率更高的代码,编译器或解释器加快了程序的执行进度。
但这只是编译器级的优化。一个单独的程序,从开始设计到生成机器码,要经历好几个阶段:设计(包括架构,设计模式等等所有将参与机器码规划的软件活动)、源代码编写(包括配置编码)、源代码编译(包括上面提到的优化)、连接等。每一个阶段都能影响最终代码的运行效率。但是“设计”是一个庞大的话题(我后面会有很多文章涉及当今软件界在设计上存在的重大误区),这里暂时不讨论。作为开博的第一篇,我想先讨论编码(因为这是藏在我心里最久的一个想法)。
如果说编译器优化是通过将局部代码翻译成效率更高的机器码来完成的,那么代码优化则是通过删除(在运行期删除)一些不必要执行的代码来完成的。
比如,一个100个case的switch语句。如果考虑条件可能发生的频率的话,显然应该将发生频率最高的条件分支放在第一个case,而发生频率最低的条件分支放在最后一个case。一个错误的排列,将导致处理器最多可能浪费约99倍的处理时间。因为执行了许多本不需要得到执行的代码!
假如给定一个任务。任务内容为给一个Web应用写一段用户认证程序。你会怎么写呢?
给五分钟时间让你先想想!
......
怎么样?想得差不多了吧?
我也想好了!所以先看看我写的程序好吧:
if ( request.getParameter("username") == null || request.getParameter("password") == null ) {
PrintWriter writer = new PrintWriter(response.getOutputStream());
out.println ( "请输入用户名与密码!" );
out.flush();
} else
......
这样的程序看似合理。因为按照java编码标准,空指针检查显然排在很重要的位置。不检查空指针的人,很可能被别的更资深的一些程序员们看作是刚入门的人。
然而,这看似合理的程序,其实是非常不合理的。
因为程序的第一行用掉两个方法调用(在绝大多数情况下,的确是两个)。而这两个调用其实却并不一定是必须的。在绝大多数情况下!
为什么?
因为这两句话检查的是HTTP客户端有没有传入username与password这两个参数。而在绝大多数情况下,这两个参数是会被传入的(至少在一个产品环境下)。在认证的第一行就检查这两个参数是否传入,就象机检时第一件事就查你有没有猪流感一样,那概率实在是太小了。因为基于目前的数据,显然患有人流感的乘客比起患有猪流感的乘客数量要大得多!更合理的流程显然是先查人流感,再查猪流感!据此,我对机场的安检效率的确有很大的意见。
类似的错误并不只在if-else句中出现。其它的分支语句如switch-case中也有这样的问题。特别是在switch语句中,大部分人喜欢按顺序写程序。比如:
switch ( i ) {
case 0:
do A;
case 1:
do B;
...
default:
do X;
break;
}
相信大部分人并没有想过这么写到底好不好。因为大部分人写程序时最担心的是它到底能不能工作。的确,在我刚入行的时候,或者说,入行后很久,甚至包括我现在见到的很多具有5年甚至7年以上工作经验的人,仍然被担忧所控制---他们总是在担心程序到底能不能运行。而很少担心程序到底能不能很好地运行。因为那很少能被人发现,或者说,很少有人能够发现那样的问题!
基于条件发生频率的编程
所有的程序最终都将被译成机器码。不管是编译式程序还是解释型程序。最终的任务只能依赖硬件来完成。这就如我们的脑子虽然负责所有的思考活动,但却实际上不可能完成任何工作一样。因为任何工作最终都要依赖于人体这个硬件来完成(因为软件其实并不是真实存在的东西。它只是一个硬件的状态集。真正完成状态转换的其实并不是软件,甚至连硬件也不是,是电!---至少在电动力计算机中,是这样的)。
程序编译器或者解释器经常会对一些经常运行的代码进行一些优化。这种优化,基于的就是不同代码运行频率并不相同的原理。通过优化运行频率更高的代码,编译器或解释器加快了程序的执行进度。
但这只是编译器级的优化。一个单独的程序,从开始设计到生成机器码,要经历好几个阶段:设计(包括架构,设计模式等等所有将参与机器码规划的软件活动)、源代码编写(包括配置编码)、源代码编译(包括上面提到的优化)、连接等。每一个阶段都能影响最终代码的运行效率。但是“设计”是一个庞大的话题(我后面会有很多文章涉及当今软件界在设计上存在的重大误区),这里暂时不讨论。作为开博的第一篇,我想先讨论编码(因为这是藏在我心里最久的一个想法)。
如果说编译器优化是通过将局部代码翻译成效率更高的机器码来完成的,那么代码优化则是通过删除(在运行期删除)一些不必要执行的代码来完成的。
比如,一个100个case的switch语句。如果考虑条件可能发生的频率的话,显然应该将发生频率最高的条件分支放在第一个case,而发生频率最低的条件分支放在最后一个case。一个错误的排列,将导致处理器最多可能浪费约99倍的处理时间。因为执行了许多本不需要得到执行的代码!