过早优化是万恶之源

本文探讨了代码优化的原则,强调正确的程序比快速但可能出错的程序更重要。提出了七个优化原则,并通过实例说明过早优化的危害。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Don’t Cut Yourself: Code Optimization as a Double-Edged Sword。中文翻译:过早优化是万恶之源。

 

代码优化的好处多多,但是这并不意味着所有的代码都需要进行优化,有时过度的优化反而适得其反——费时、费力、不讨好。 

“现代计算机科学的鼻祖”Donald Knuth曾说过“过早的优化是万恶之源”,因为:让正确的程序更快,要比让快速的程序正确容易得多。文中讲了7个原则,简单罗列如下:

1.  究竟要优化什么? 

2.  选择一个正确的优化指标 

3.  优化在刀刃上 

4.  优化层次越高越好 

5.  不要过早优化

6.  依赖性能分析,而不是直觉 

7.  优化不是万金油 

 

更详细的大家可以看英文: http://blog.smartbear.com/programming/dont-cut-yourself-code-optimization-as-a-double-edged-sword/

  

为什么把这个话题拿出来讨论下,因为我现实中发现过早优化,其实是一个非常容易犯的错误。举几个例子:

1、数据库优化中,为了性能优化,放弃通用性,把SQL全部用C语言重写,这种技术是不可能得到发展的。

2、Hadoop领域里面,Tez/stringer为了解决hive性能,只是将M/R通过DAG作业来替代,将多个作业用一个作业来替代,减少中间过程。但是实际上hive用在查询上,除了M/R效率底下外,还有进程启动代价太高,以及最主要的是数据存储不是专门为分析场景准备的。所以我预计如果Tez/stringer只是按照目前的思路优化,最后肯定昙花一现。

3、Shark,hive on spark。简单的将hive拿到spark上来,从最新资料来看,DataBricks 已经被放弃了shark,转而将重心放到 Spark sql上面来。

 

 

不要为了优化而优化,优化应该先做好顶层设计,再落地到具体的技术细节,否则优化出来的东西,不会有长久的生命力。



 

子任务4:确定程序所需的数据结构 经过以上分析可知,为了得到公历日期对应的星期、农历日期、节气、节日,程序需要预知以下数据: 1月1日的星期:weekday_2025; 1月1日的农历日期:lunar_start_date_2025; 每个农历月份的天数:lm_days_2025; 是否有农历闰月。若有,是几月:lm_leap_month_2025; 每个节气对应的公历日期:solar_terms_2025; 每个节日对应的公历日期或农历日期:feasts。 查阅得到这些信息,以合适的形式写进程序中即可。(把数据写进程序,称为数据“硬编码”,是比较糟糕的做法。但作为最初的探索,却是最快能够看到结果的。后续将分离程序与数据,解决数据“硬编码”的问题) 其中,除了weekday与feasts之外,其余都是每年有一份数据。其中,下一年的weekday可以轻而易举地按照上一年的weekday计算得到,因而只需要保存其中某一年的weekday,而不需要每年都保存一份。 节日feasts与年份无关,比如每年的国庆节都是10月1日、春节都是正月初一,因而也无需每年都保存一份。 其余数据,均与年份有关,需要每年保存一份。 日期表示 日期,是程序中的基本概念,完整的日期包含公历日期、农历日期、星期、节气、节日等多项成员。比如,可以表示为: struct u_date_t { short year; char month; char day; }; struct full_date_t { struct u_date_t solar_date; struct u_date_t lunar_date; char weekday; char solar_term; char feast; }; 这样做当然是可以的,但可能使程序代码显得冗长。由于T1任务中并没有用到full_date_t结构体的情形,为使程序代码简短,暂不考虑这样表示日期,但保留struct u_date_t结构体作为公历日期与农历日期统一的表示方式。 关键是要解决第2步中“预知数据”的表示及存储。 计算星期几所需的数据 每年1月1日是星期几,可以从1970年1月1日是星期几计算得到。因而,只需要存储weekday_1970即可。 const char weekday_1970 = 4; /* 1970年1月1日是星期四 */ 节日对应的日期只需要月、日,可以表示为: const struct feast_t { enum {SOLAR, LUNAR} type; struct u_date_t u_date; const char * name; } g_feasts[] = { {SOLAR, {0, 1, 1}, "元旦"}, {LUNAR,{0, 1, 1}, "春节"}, {SOLAR, {0, 10, 1}, "国庆节"}, {LUNAR, {0,5,5}, "端午节"} }; 更多的节日按同样格式加到结构体数据中即可。借用之前定义的struct u_date_t结构体时,此处用不到其中的year成员,初始化为用不到的0即可。 每年需要单独存储的数据 以下4个数据每年需要单独存储: 1月1日的农历日期:lunar_start_date_2025; 每个农历月份的天数:lm_days_2025; 是否有农历闰月。若有,是几月:lm_leap_month_2025; 每个节气对应的公历日期:solar_terms_2025; 可以放到同一个struct中: struct lunar_info_t { struct u_date_t lunar_start_date; char lm_days[12]; /* 12个农历月每月的天数 */ char lm_leap_month; /* 农历闰月的月份序号,无闰月时为0 */ char solar_terms[24]; /* 24个节气对应的公历日期 */ } g_lunar_info[130] = {……}; /* 从1970~2099共130年 */ 完成以上两个struct之后,发现u_date_t.year到处都用不到,删除该成员,相应修改初始化数据。注意:这一步必须要等到最后再做,不要过早地下结论。这旧工作属于“优化”的范畴,过早优化万恶之源。首先按部就班、不厌其烦地完成可以运行的程序,绝不在程序还不能运行之前开始优化,才是编程的正确做法。 基础数据 除了参与计算的数据之外,星期、农历月份与日期、节气在程序内部都使用整数表示,而输出时都需要转换成字符串,这类数据统一放到基础数据中。 const stuct base_info_t { const char weekday_1970; const char * wd_name[7]; const char * lm_name[12]; const char * ld_name[30]; const char * st_name[24]; } g_bi = { .weekday_1970 = 4, .wd_name = {"星期日", "星期一", "星期二","星期三","星期四","星期五", "星期六"}, .lm_name = {"正月", "二月","三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"}, .ld_name = {"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"}, .st_name = {"小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分","寒露", "霜降", "立冬", "小雪", "大雪", "冬至"} }; 有了如上分析和数据基础,可以得到能够正确计算出每天是星期几,以及2025年每天对应农历日期的程序。 写出完整的C语言程序代码。
最新发布
06-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值