解惑

解己之惑,解人之惑

标签:精华 (第1页共2页)

BASH的一些知识总结

不知不觉一个月就过去了,发现什么都没有写,趁放假补一下功课吧。

最近的几个月搞了不少BASH的东西,因为我把系统的升级功能重写了下,原来的代码实在是惨不忍睹。总结几个在重写的过程中的BASH知识点。

  1. 根据变量名得到变量值,这个在使用动态功能的时候特别有用,其实也很简单 ${!variablename}
  2. 使用管道后得到管道的某个输出的exit code:${PIPESTATUS[0]}, 0就是拿管道最开始的那个命令的返回值,如果不使用这个,那么exit code永远是0
  3. 使用“可以得到命令执行的输出结果,比如`hostname -s`,另外一种方式是$(hostname -s)
  4. 可以直接使用某个方法的返回值做if的条件判断,比如 if my_function; then
  5. 在引用变量值的时候尽可能给变量加上引号,特别是想把变量值往其他的方法传的时候,例如 my_function “$my_variable”,因为使用的那个变量值包含空格之类的就可能不是你期望的结果了。
  6. 得到当前代码行的行号:$LINENO
  7. 方法只能返回int值,不能返回string,所以方法想返回string的时候只能借助global的变量
  8. exit code的返回值范围是0到255
  9. 放在双引号里的变量会被解析,如果不想被解析,就用单引号
  10. 只有方法里面的变量才能定义为local,最外层的变量只能是global,方法里面定义的变量默认也是global
  11. 在条件判断里面做字符串大小比较,<前面要加个\:if [ “$onestring” \< “$anotherstring” ] ; then
  12. 把一个local变量的值赋值给一个global变量的时候必须在local变量前加$:globalvar=$localvar
  13. 在复杂功能里面尽量不要用xargs,因为它会起子shell,变量赋值不能传到外面的功能,尽可能用while do
  14. 做数字运算其实都是个没那么简单的事情:let “filecount-=1″,变量减一操作,其它类似,使用let加上运算表达式
  15. 判断是否是空值:if [ -z $somevar] ; then
  16. 需要把多行的语句放到一行的,应该换行的地方加个分号
  17. 方法后面的参数可以接其它的方法,后面的方法甚至还可以带参数,只要在方法里面调用”$@”就可以
  18. 获得上一个语句的返回值:$?
  19. 循环处理数组中的每个值:for value in “${my_array[@]}”
  20. 判断一个变量值是否设置了:if [ -z ${myvar+x} ]; then echo “not set”; fi
  21. 如果要计算两个数并赋值给其他的变量:thirdvar=$(expr $firstvar + $secondvar)
  22. 条件判断中的字符判断直接用=,数字比较就得 -eq, -gt等,文件目录判断 -f,-d
  23. 计算数组的大小:${#myarray[@]}
  24. 把数组里面的值拼成字符串,可以使用”${nodes[@]}”,但是如果你要把这个作为方法的参数传递则不行,必须先把这个结果赋值给一个变量再传递那个变量值:allvalues=”${myarray[@]}”; my_function “$allvalues”

好像还不少,任何一个语言,要用好都不容易,但是如果你知道自己要什么,其实也不难,有google啊。

智能化和极简化

曾几何时,企业级软件是一个非常高大上的名词,而难用是后面的潜台词,从部署到配置到使用,非常的受诟病,但是没办法,在软件开发不那么成熟的时代,能够有能力组织开发出如此复杂的软件的就那么几个公司,能够做出来已经很不错了,加上采购者和使用者完全是两拨人,使用者的话语权太低了,所以以用户为导向的重要性虽然在产品开发的过程中被重视,但是视角完全是错误的,重点完全在功能的完整性、多样性和灵活性上。当互联网真正崛起,SaaS被广为接受,服务化被广泛采用后,我们才发现他们的玩法不一样了,因为直接面对的是最终使用者,最终使用者的口碑传播效应得到了极大提升,以使用者作为真正用户的视角才被纠正,所以各种讨好最终使用者的特性才被重视起来了,早期一个很重要的概念就是傻瓜化,这个,乔布斯的iPhone也是居功至伟的,但是我更愿意叫这个为智能化,伴随而来的还有极简化,核心思想就是尽最大可能减少用户的思考和使用门槛,上手就能够用而且用的比较顺畅。由于我以前也搞过自己的社区,深深知道这个有多么的重要,而很不幸的是这么多年都在大公司,很多思想并不被接受,终于,从前几年开始,这些风终于刮到了企业级这个漆黑的领域。从3年前做的一键升级整个集群,使得原来一个40台节点集群的升级时间从7天缩短到8个小时(大部分其实是升级准备及数据库,服务器升级半个小时内就可以搞定,并行的),而且基本不需要人工干预(除非客户有自己的定制化配置),为此Support的头头还专门写信表扬过,到现在,新的产品的一个极大的重心也是在智能化和极简化上,包括安装和升级,总算开始和自己一贯的思想理念一致了,特此纪念下。。。

育儿心得之一:信任

这个是育儿中最重要的一个因素,如果和孩子不能建立信任关系,那么后面的什么东西沟通起来都会事倍功半,你说得再好,孩子不相信你说的话,一切的话语都没有了意义。
那么如何建立信任呢?其实也很简单:自己说到做到,言出必行,不欺骗(哪怕是善意的)。
这些说起来简单,但是其实在实际中还是有难度的,很多时候为了求快,就会“哄”孩子,开始的时候可能会成功几次,但是这种方法可能就能管用几次,因为孩子在快速成长,也在和小伙伴交流,一旦他们意识到你是在“哄骗”他们,那么后面的哄骗就不起作用了,连带其它的一些话也不会相信,或者他们也会模仿你,先答应下来但是并不会真正去完成,这个时候就是自酿苦酒了。这种问题在老一代中非常常见,也有很多家长也是这个问题。
在生活中,和孩子商定的东西一定要尽可能兑现,如果实在无法兑现,也一定在第一时间和他解释清楚并商量一个补偿的办法,从而完成自己的承诺。这个就像你要求他做完作业,他答应了先玩什么什么,但是玩得忘乎所以最后没有完成自己的作用,那么就要要求他自己给出合理的补偿方案,比如第二天补做并加一定的量,而且还得完成当天商定的作业内容,如果还做不到如何惩罚,这个惩罚,可以最后到挨打,一定要让孩子意识到承诺是很严肃的事情,答应的事情都要尽可能完成,否则要承担相应的后果,无论是谁都应该这样。

你是否精益求精?

有时候感觉自己做事情太精益求精了,比如说写代码,我写的时候不注意格式,但是写完存盘之前肯定要格式化一下再存盘,另外也很注意是不是有警告信息,我自己维护的代码是肯定没有警告的,不使用已经deprecated的方法,不导入任何不使用的包或者类,如果一个类实现了Serializable,那么一定要给那个类生成一个serialVersionUID,如果看到其它的不符合这些的代码,就感觉有点厌烦,好在我一般看的开源的一些代码做得都还不错,厌烦得时候并不是很多,但是项目里面得代码就不太一样了,一共有9577个警告,这个还是关闭了XML和JSP的校验的结果,另外我们的资源文件里面的重复的主键非常的多,使用我常用的那个资源编辑插件打开的话也是满眼的警告。
虽然有时候感觉自己这样有点吹毛求疵,有时候为了达到自己的标准要做很多“不太必要”的工作,会很累,但是如果是自己经常需要接触的代码,发现这些问题的话我还是会不厌其烦的把它修改到让自己满意为止。
这个好像就是心理学上所谓的强迫症吧。

Google面试题解说性能之八:工欲善其事必先利其器

按照原先的计划,这个系列只应该有四篇,但是后来打算多写一些,把这个问题研究透彻,所以出现了总结篇先于其它篇的情况。
这次我们按照总结篇中提到的方法实际演示下代码覆盖工具如何帮助我们优化程序提高性能,先给出我们未经好好优化的程序:
package com.jiehoo.util;

public class GoogleFn {
private static final int MAX = 2600002;

private static long start = System.currentTimeMillis();

private static int[] bases = new int[15];

private static int[] values = new int[15];

static {
bases[0] = 0;
bases[1] = 10;
values[0] = 0;
values[1] = 1;
for (int i = 2; i < values.length; i++) {
bases[i] = (int) Math.pow(10, i);
values[i] = i * (int) Math.pow(10, i – 1);
}
}

阅读全文

Google面试题解说性能之七:缓存中间结果

上次已经说了fn的实现不能用来查找符合条件的n,因为这样做比前面的第一个例子中的性能比较差的那个还要差,原因就是有太多的重复计算,如果只是计算一个指定的数的结果,那么那个实现是无与匹敌的。但是我们是讲的性能优化,所以,我们就用它来做,放慢速度,然后使用其它的技巧来提高性能,这次的方法就是简单的使用缓存:
public class GoogleFn {
private static final int MAX = 2600002;

private static long start = System.currentTimeMillis();

private static int[] bases = new int[15];

private static int[] values = new int[15];

private static int fn(int number) {
if (number < 10) {
return number > 0 ? 1 : 0;
}
String s = number + “”;
int length = s.length();
int end = Integer.parseInt(s.substring(1, length));
int x = s.charAt(0) – ‘0’;
int result = 0;
if (x == 1) {
result = values[length – 1] + fn(end) + (end + 1);
} else {
result = values[length – 1] * x + bases[length – 1] + fn(end);
}
return result;
}

阅读全文

Google面试题解说性能之六:数学显神威

其实很多问题一旦涉及到数学问题或者数据处理密集型问题,那么最终显现神威的就是数学公式,这个面试题也是这类问题,所以如果我们能够推导出一个数学公式就是最理想的,在前面的例子中,我们进行了一些深入的分析,根据前面的例子,你可能会尝试把步长从100扩展到1000或者10000,但是实际上这个方法遇到了瓶颈,因为循环嵌套的层次太多,计算公式太复杂也会导致问题。如果我们最开始尝试的时候把全部的f(n)的结果打印出来,你会发现这样的内容:

  1. f(9) = 1
  2. f(99) = 20
  3. f(999) = 300
  4. f(9999) = 4000
  5. ……

这个是我们的第一个规律:位数乘以((位数-1)的10的次方)。
根据这个f(n)的说明,我们定义另外一个方法x(n),它的定义就是n这个数包含的1的个数,例如x(1)=1,x(2)=0,x(11)=2,那么我们可以把f(n)展开为:
f(n)=x(0)+x(1)+……+x(n)
同时我们可以把x(n)也展开,假设n=XYZ,那么x(n)的展开式为:
x(n)= x(X)+x(Y)+x(Z)
也等于:
x(n)= x(X)+x(YZ)
再结合上面的规律我们就可以推导出一个规律了,先用例子来说明,以106为例:
f(106) = x(0)+…+x(99)+x(100)+…+x(106) = f(99) + x(100)+…+x(106)
f(99)我们使用上面的第一个规律很容易计算得到,那么后面的这7个数包含多少个1呢,其实也很简单,应用可能小学就学过的公因子概念,当然这里不是真正的公因子,而是这些数里面包含的1的个数相同的部分,结合x(n)的展开式,我们进一步推演出:
f(106) = f(99) + x(1)+ x(00)+x(1)+x(01)+…+x(1)+x(06) = f(99) + x(1) * (6+1) + x(00) + .. x(06) = f(99) + x(1) * (6+1) + f(6)
这样计算就很简单了,不是吗?
好,再看看f(345)的情况,有点不太一样:
f(345) = x(0)+…x(99)+x(100)+…+x(199)+x(200)+..+x(299)+x(300)+…+x(345)= f(99) + x(1) * (99+1) + f(99)+ x(2)*(99+1)+f(99)+x(3)*(45+1)+f(45)
这个例子足够典型了吗?看到规律了吗?
给定一个数n,假设最高位为x,除去最高位的数字为y,位数为z,那么
如果x=1,那么f(n)等于f(pow(10,z-1)-1)+(y+1)+f(y)
如果x>1,那么f(n)等于f(pow(10,z-1)-1)*x+pow(10,z-1)+f(y)

转换为代码就是:

private static int fn(int number) {
if (number < 10) {
return number > 0 ? 1 : 0;
}
String s = number + “”;
int length = s.length();
int end = Integer.parseInt(s.substring(1, length));
int x = s.charAt(0) – ‘0’;
int result = 0;
if (x == 1) {
result = (length – 1) * (int) Math.pow(10, length – 1 – 1) + fn(end)
+ (end + 1);
} else {
result = (length – 1) * (int) Math.pow(10, length – 1 – 1) * x
+ (int) Math.pow(10, length – 1) + fn(end);
}
return result;
}

你可以运行试试这个公式是否准确。
最后需要强调一下的是,这个方法可以快速的计算给定的一个数的f(n)的结果,但是如果用一个简单的循环来查找符合f(n)=n的结果是不合适的,这个我会另外再谈的。

  1. Google面试题解说性能之一:字符串运算VS数字运算

  2. Google面试题解说性能之二:分析问题

  3. Google面试题解说性能之三:不要小看循环中的任何一个语句

  4. Google面试题解说性能之四:优化无止境

  5. Google面试题解说性能之五:人比电脑聪明

  6. Google面试题解说性能之六:数学显神威

  7. Google面试题解说性能之七:缓存中间结果

  8. Google面试题解说性能之八:工欲善其事必先利其器

  9. Google面试题解说性能之总结

Google面试题解说性能之五:人比电脑聪明

在例子四的基础上,我们可以进行更加深入的分析,我们还是以100为例,我们其实在大部分情况下可以省略循环,如果数字的百位数以上包含1的个数为0,而十位数不为1,那么当个位数大于1以后,我们可以中断底层的循环,这样我们又节省了很多的运算:
public class GoogleFn {
private static int MAX = 1320000000;

private static int MAX2 = MAX / 10;

private static int MAX3 = MAX2 / 10;

private static int count(int n) {
int count = 0;
while (n > 0) {
int mod = n % 10;
if (mod == 1)
count++;
n = n / 10;
}
return count;
}

阅读全文

Google面试题解说性能之总结

呵呵,说了这么多,到底怎么优化性能还是没有说多少,而且一个产品的代码比这个例子复杂得多,怎么才能优化产品代码呢?
很简单,找到性能瓶颈,而大部分的性能瓶颈都有一个特点:被执行的次数太多。一个耗时2分钟的操作,如果系统运行一天才需要运行一次,那么我们根本就不要去理会它,如果一个操作耗时2秒,但是一般运行一天它要被执行几千亿次,那么你就要小心了。
如何才能知道系统中的哪些代码被执行的次数最多呢?有很多工具可以,有的是挂到系统上一起运行,有的是可以单独运行,但是我推荐的方法就是使用单元测试工具和代码覆盖工具,运行所有的单元测试,查看代码覆盖报告中被执行的次数最多的那些语句,看看他们是否可以被优化,或者可以被减少执行的次数。
可以参考我以前的一些日志:
Ant+JUnit+Cobertura
成功提高20倍性能

很多情况下,找到性能的瓶颈并不是很困难,真正困难的是如何进行优化。这个没有通用的解决方法,只能结合具体的问题具体解决,一个大部分情况下有效的方法是使用某种缓存机制(实际上,我的第二个例子也是使用了缓存机制,把运算结果缓存了9次)。

  1. Google面试题解说性能之一:字符串运算VS数字运算

  2. Google面试题解说性能之二:分析问题

  3. Google面试题解说性能之三:不要小看循环中的任何一个语句

  4. Google面试题解说性能之四:优化无止境

  5. Google面试题解说性能之五:人比电脑聪明

  6. Google面试题解说性能之六:数学显神威

  7. Google面试题解说性能之七:缓存中间结果

  8. Google面试题解说性能之八:工欲善其事必先利其器

  9. Google面试题解说性能之总结

Google面试题解说性能之四:优化无止境

其实在例子二的基础上,我们进一步的分析,可以把缓存10个结果换成缓存100个结果,性能可以得到进一步提升:
public class GoogleFn {
private static int MAX = 13200000;

private static int MAX2 = MAX / 10;

private static int MAX3 = MAX2 / 10;

private static int count(int n) {
int count = 0;
while (n > 0) {
int mod = n % 10;
if (mod == 1)
count++;
n = n / 10;
}
return count;
}

阅读全文

更早的文章

© 2019 解惑

本主题由Anders Noren提供向上 ↑