Hello World
Spiga

浅谈代码的执行效率(1):算法是关键

2010-01-07 17:14 by 老赵, 13557 visits

前一段时间在博客园里看到这样一篇文章,那位兄弟谈到程序效率的关键是“简短”。他说,“程序越简短,其可执行代码就越少,就越有效率”,而在编写程序的时候,“要尽量改进我们的算法,而改进算法中最重要的一条,就是减少语句”。这句话从表面上似乎正确,但我认为性能这问题不能用“简短”这种方式去思考,否则会进入一些误区。我整理了一下思路,希望可以从几个方面把详细谈一下这个问题。

首先,如果说“简短的代码效率高”,那么什么叫作“简短”呢?姑且理解为“代码少”吧。如果“代码少”能够得出“效率高”,那我认为还需要其他一些条件,例如:

  • 代码完全是顺序执行的
  • 每行代码的效率相同

但是,这对于使用高级语言的程序员来说,这两条基本上都无法成立,因为:

  • 代码中有条件跳转(如循环),因此一段简短的代码最终执行的“次数”可能比一段复杂的代码要多。这是很常见的情况,并非如那位兄弟所说“两三行代码写出死循环”这样的“特例”。
  • 每行代码的效率几乎不可能相同。试想一个i++和一个方法调用,他们的性能开销怎么可能相同呢?再者,这个特性并非是“高级语言”特有的,连CPU指令也是一样(以后我们再来详谈这个问题)。

其实这就是目前编程工作中不可能回避的现状,那就是高级语言并不直接反映出机器的执行过程。同时,我们不能从代码表面的简短程度来判断程序效率的高低。正如那位朋友所谈,影响程序的关键因素之一是算法的选择,但我不同意他说算法优化的关键在于“减少语句”。从一定程度上讲,选择高效的算法的确是为了减少指令执行的“总量”,但它并不是如那篇文章所说通过“少一些变量”,“少一些判断”来进行的,而是在于大幅的逻辑改变来减少总体的操作。

事实上,我们很容易便能举出代码简短,但是性能较差的算法。就拿最常见的排序算法来说,冒泡排序不可谓不简单:

点此展开

而快速排序与之相比实在是复杂太多了:

点此展开

OMG,为什么快速排序会那么复杂?其原因在于,快速排序的性能关键之一,在于选择一个好的中轴(pivot)元素,选择一个好的中轴元素可以尽可能减少的交换操作的次数,以此提高算法的效率。而冒泡排序,做法的确“简短”,但是其性能在大部分场合都不如快速排序来的好。而且,同样是快速排序,也可以有多种实现方法,有的语言还可以实现地非常简单,如Haskell:

qsort [] = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)

这便是Haskell版的快速排序,够短吧。但是它的效率不如之前那段长长的C#——当然,这么比可能不公平,因为两者使用的数据结构不同,C#基于数组进行排序,而Haskell却使用的是不可变的单向链表。

算法是效率的关键,但选择合适的算法也并非是一件十分容易的事情。我们都知道一个算法它有时间复杂度,空间复杂度等等,但这些是什么呢?是从数学角度得出的“理论值”。一个算法的时间复杂度,考虑的是算法的时间随规模增长的变化规律,它能确保的只是“当规模大到一定程度时”,时间复杂度低的算法效率肯定相对较高。但是在实际开发过程中,我们遇到的数据量往往都是有限的,其形式甚至还有一定规律可循。因此,“理论上”时间复杂度低的算法,并非时时刻刻都有上佳表现。

例如,对于一个已经排序的数组来说,冒泡排序的性能无人能敌;对于一个高效的快速排序算法来说,如果元素数量少于一个阈值时就会使用插入排序(因为快速排序时间复杂度的常数较大);再比如,一个算法使用空间换时间,但是空间一大,物理内存中的数据可能就会被放到磁盘交换区上(也有人把它叫做虚拟内存,但是我不认同这个叫法),此时读取时可能就要从硬盘上重新加载数据页,性能可能就更低一些了。说个更近的,有时候每次都创建对象,性能反而比缓存起来要高,不是吗?

因此我认为,无论怎么样,“简短”不应该是评价一段代码是否高效的因素。您可能会说,算法对性能来说自然很关键,但是这里说的“简短”不是指算法的改变,而是指在算法相同的情况下,少一些赋值,判断操作等等,这样指令少了性能不是更高了吗?不过我的考虑是,既然算法已经确定了,那么究竟有哪些地方值得我们减少一些赋值或判断操作呢?即便这样的确有效果,但我想,如果保留合理的赋值和判断如果可以让代码更清晰,那就不要进行如此“手动”而“原始”更“想当然”的优化吧。清晰、正确比高效更重要。

最重要的是,没有Profiling,一切优化都是徒劳的。如果您认为减少赋值和判断可以有效提高性能,那么也用Profiling来证明一下,不是吗?当然,我并没有说一些细节上的调整对性能影响不大,我接下来也会讨论直接对汇编进行的优化。但是,即使我们有需要优化汇编的场景……它们基本上也不是靠所谓减少赋值和判断来起到效果的。

最后补充一句:这篇文章里我谈到的“算法”是指广义的“实现方式”,通俗地讲便是“代码的写法”。而“冒泡排序”等面向指定问题的解法,我把它称之为“经典算法”。在这里,我们还是加以区分吧。

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

60 条回复

  1. 老赵
    admin
    链接

    老赵 2010-01-07 17:14:00

    “缓存”与“指令”可能拆成两部分来写……但是目前还在担心单独写的话内容不够丰富,继续收集素材中……

  2. Cocoo
    *.*.*.*
    链接

    Cocoo 2010-01-07 17:19:00

    言之有理.

  3. winter-cn
    *.*.*.*
    链接

    winter-cn 2010-01-07 17:21:00

    最重要的是算法复杂度的效率差异是硬件、编译优化、缓存等手段无法弥补的

  4. winter-cn
    *.*.*.*
    链接

    winter-cn 2010-01-07 17:22:00

    最简短的低效:
    while(true);
    哈哈哈

  5. 老赵
    admin
    链接

    老赵 2010-01-07 17:23:00

    @winter-cn
    能不能说“无法弥补”,我觉得还值得商榷。
    因为比如缓存,其实是算法所考虑的一部分,例如如何利用好Locality是算法要考虑的。
    此外,某些算法也会利用起编译器的优化能力,下一篇我会简单谈谈这个问题的。
    硬件的话——硬件效率可能是无法弥补的,但是体系结构也是硬件的一部分嘛,算法会涉及到滴。:)
    总之,算法是个很关键的东西,涉及很多东西,优化不是靠少几个判断什么就有效果的……

  6. 老赵
    admin
    链接

    老赵 2010-01-07 17:25:00

    winter-cn:
    最简短的低效:
    while(true);
    哈哈哈


    这是特例,咱要考虑平时编程中的状况,嘿嘿。

  7. EricZhang(T2噬菌体)
    *.*.*.*
    链接

    EricZhang(T2噬菌体) 2010-01-07 17:29:00

    让我想起大一时的一件事情。当时我们C语言老师也是说简短的代码更有效率,我说不一定。
    后来争辩起来,于是我让她测试下面两段程序的效率:

    code1:

    int i;
    int a[10];
    for (i = 0;i < 10; i++) {
    a[i] = i;
    }

    code2:

    int a[10];
    a[0] = 0;
    a[1] = 1;
    a[2] = 2;
    a[3] = 3;
    a[4] = 4;
    a[5] = 5;
    a[6] = 6;
    a[7] = 7;
    a[8] = 8;
    a[9] = 9;

    结果我就不说了,各位有兴趣可以把这两段代码各执行10万次比较一下,呵呵。

    一般来说,简洁的代码不一定效率高,但可读性可能会好一些。因此在效率要求不是很严格的环境下,可以追求一下代码的简洁。
    但如果单纯认为人读起来效率高的代码机器执行效率也高,就有问题了。

  8. winter-cn
    *.*.*.*
    链接

    winter-cn 2010-01-07 17:32:00

    @Jeffrey Zhao
    这样说吧 大部分优化都是在规模的基础上乘一个小于1的系数 而算法决定运行时间和规模的函数关系 如果算法复杂度本身不会变化 那么在其它方面做得优化效果不会很明显了
    反正意思是这样的

  9. Jeff Chow
    *.*.*.*
    链接

    Jeff Chow 2010-01-07 17:32:00

    作者似乎对曲解了所引用文章的意思,或者没看仔细。

    下面还写了“但是并不总是是程序短,时间就短”。

    个人觉得原文并没有想要表达“程序越简短,就越有效率”的意思。

  10. 老赵
    admin
    链接

    老赵 2010-01-07 17:35:00

    Jeff Chow:
    作者似乎对曲解了所引用文章的意思,或者没看仔细。

    下面还写了“但是并不总是是程序短,时间就短”。

    个人觉得原文并没有想要表达“程序越简短,就越有效率”的意思。


    读了几遍,包括评论,也和其他人讨论这篇文章了,所以相信没有曲解,呵呵。
    如果真曲解了,反正我的目的不是为了批判那篇文章或是作者,只要我的内容是正确的就好了,呵呵。

  11. 灵感之源
    *.*.*.*
    链接

    灵感之源 2010-01-07 17:35:00

    老赵失业了?

  12. 老赵
    admin
    链接

    老赵 2010-01-07 17:37:00

    EricZhang(T2噬菌体):
    一般来说,简洁的代码不一定效率高,但可读性可能会好一些。因此在效率要求不是很严格的环境下,可以追求一下代码的简洁。
    但如果单纯认为人读起来效率高的代码机器执行效率也高,就有问题了。


    我觉得还是不要谈“简洁”两个字比较好,直接谈“清晰”,比如原文说要少一些语句啊,判断啊等等,我觉得反而难以有可读性了。
    你说“人读起来效率高”不等于“机器执行效率”,我举四肢赞成,呵呵。

  13. 老赵
    admin
    链接

    老赵 2010-01-07 17:38:00

    灵感之源:老赵失业了?


    唉,是啊……

  14. pangxiaoliang[北京]流浪者
    *.*.*.*
    链接

    pangxiaoliang[北京]流浪者 2010-01-07 17:43:00

    还有递归和非递归算法,也是个显著的例子,老赵跳槽了,月薪xxxxxx万。

  15. 老赵
    admin
    链接

    老赵 2010-01-07 17:46:00

    pangxiaoliang[北京]流浪者:老赵跳槽了,月薪xxxxxx万。


    没错,可惜这货币单位比较可怜……

  16. pangxiaoliang[北京]流浪者
    *.*.*.*
    链接

    pangxiaoliang[北京]流浪者 2010-01-07 17:48:00

    老赵去日本了?莫非?是不是被日本妞魅惑了~

  17. 老赵
    admin
    链接

    老赵 2010-01-07 17:49:00

    @pangxiaoliang[北京]流浪者
    如果是日币我也大发了好不好

  18. pangxiaoliang[北京]流浪者
    *.*.*.*
    链接

    pangxiaoliang[北京]流浪者 2010-01-07 17:51:00

    @Jeffrey Zhao
    难道是卢布?越南盾?老赵公布答案吧,呵呵~

  19. 幸存者
    *.*.*.*
    链接

    幸存者 2010-01-07 17:51:00

    pangxiaoliang[北京]流浪者:还有递归和非递归算法,也是个显著的例子


    算法与递不递归是正交的,同样的算法既可以用递归实现,也可以用循环实现,效率差别不大,很多高效的算法都是用递归实现的,例如经典的快速排序算法。

  20. pangxiaoliang[北京]流浪者
    *.*.*.*
    链接

    pangxiaoliang[北京]流浪者 2010-01-07 17:55:00

    @幸存者
    可是,递归这个东东我以后不感用了,前些时间做神经网络的OCR,结构用递归报错了,训练次数太多,栈撑不住了。

  21. Icebird
    *.*.*.*
    链接

    Icebird 2010-01-07 18:07:00

    作为开发人员,需要在执行效率与开发效率中找到平衡点,一味的去追求开发效率或执行效率都是不可取的。

  22. 老赵
    admin
    链接

    老赵 2010-01-07 18:19:00

    pangxiaoliang[北京]流浪者:
    @幸存者
    可是,递归这个东东我以后不感用了,前些时间做神经网络的OCR,结构用递归报错了,训练次数太多,栈撑不住了。


    没什么肯定不敢用的,总归有合适的时候可用,你的情况只是一个特例了,并不是一定会有那么深,hoho。

  23. 老赵
    admin
    链接

    老赵 2010-01-07 18:20:00

    幸存者:

    pangxiaoliang[北京]流浪者:还有递归和非递归算法,也是个显著的例子


    算法与递不递归是正交的,同样的算法既可以用递归实现,也可以用循环实现,效率差别不大,很多高效的算法都是用递归实现的,例如经典的快速排序算法。


    递归不会影响时间复杂度,但是空间复杂度是会上去的,算法的Locality可能就会降低(于是性能降低),常数会比较大,这是由体系结构决定的,呵呵。
    但是,开销较大不代表不能用,因为换种方式也不一定能省下来,递归的这方面性能开销也可能不是关键,而递归可能也会非常清晰。

  24. BreezeWoo
    *.*.*.*
    链接

    BreezeWoo 2010-01-07 18:46:00

    恭喜老赵,贺喜老赵,今天排名第一了。忍了很久了吧,哈哈。

  25. 老赵
    admin
    链接

    老赵 2010-01-07 19:03:00

    @BreezeWoo
    是1月1号开始的事情了……谢谢支持……

  26. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-01-07 20:27:00

    呵呵,老赵最近可不可以写点F#系列的东西啊,并行编程未来的主流啊

  27. 老赵
    admin
    链接

    老赵 2010-01-07 20:39:00

    @温景良(Jason)
    写入们的东西啊?主要是不知道怎么写,容易变成翻译书。

  28. 小城故事
    *.*.*.*
    链接

    小城故事 2010-01-07 20:50:00

    嘿嘿,比较了不少,还是偶的快速排序最好看

  29. 老赵
    admin
    链接

    老赵 2010-01-07 20:53:00

    小城故事:嘿嘿,比较了不少,还是偶的快速排序最好看


    嘿嘿,比Haskell的还好看?

  30. 卡通一下
    *.*.*.*
    链接

    卡通一下 2010-01-07 20:55:00

    效率一词从广义上好理解,要是从狭义上则各有不同!

    从客户体验上说,程序操作简洁,运行流畅,维护便捷,运行成本低廉,这都是效率。

    从代码上说,我是认可好的算法能带来高的效率。那什么是好的算法呢?这就因人而异,因事而异了。这是因为有些好的算法,给客户的感受并不明显。

    就我做数据类项目而言,对数据库的合理设计,才是提高运行效率的关键。

  31. 老赵
    admin
    链接

    老赵 2010-01-07 20:59:00

    @卡通一下
    说到客户体验你就扯远了,这里明显就是谈代码的执行效率了,操作简洁等用户体验上的不在讨论范围内。
    你说的数据库的合理设计,自然也是提高“程序”运行效率的关键。我这里主要还是谈——你我都知道的那“代码”效率。
    总之,要提高效率,首先要Profiling,然后找到有针对性的地方进行优化。
    我这几篇文章的目的是想说明“简短”的程序不能说明任何效率上的东西。

  32. JimLiu
    *.*.*.*
    链接

    JimLiu 2010-01-07 21:08:00

    又谈优化,哈哈
    我曾经想过写一篇文章叫《有多少程序值得优化》,想围绕哈弗曼原理做一些探讨,不过后来不知道为什么就搁浅了。

  33. Mr.d
    *.*.*.*
    链接

    Mr.d 2010-01-07 21:13:00

    听说老赵去了盛大呀,吼吼,牛人呀

  34. 算法城管
    *.*.*.*
    链接

    算法城管 2010-01-07 21:15:00

    算法的最重要意义还是给一个解决问题的思路。
    而不是效率。

    对于日常开发来说,为了追求所谓的“高效率”而不断“优化”代码,这种做法实际上比较少,因为性价比不高。

    但是将代码写的更加通俗,简单易明,又不会太多低效率,还是非常有现实意义的。

    有些时候,通俗易明往往就是相对不错的算法,当然有些时候不是这样。因为我们所处理的数据规模往往是异常小的,因此算法效率的追求就显得没那么重要。

    对于大部分程序员来说,算法的学习更重要的是知道解题技巧,如何让思路清晰,保证没有逻辑错误和减少累赘的地方,这才是关键。


  35. 卡通一下
    *.*.*.*
    链接

    卡通一下 2010-01-07 21:24:00

    楼主,不好意思,有点扯歪了。呵呵。

    说一说代码吧,也是过去的事情。

    有一次做考核程序。其中,使用了条件语句。我连续用了50多组if,这是同一种逻辑下的50多种不同条件。同事看后,说为什么不用case,那样效率更高。当时,自己也是气盛好强,就是用if,看看客户是不是就觉得慢。当然,客户也没感觉出什么。

    我说这个并不是说用if就是合理,而是说现在很多人一门心思钻牛角尖,对别人不以为然的事情大做文章,甚至斥之为垃圾代码。

    也许又扯歪了!

    哈哈...

    刚刚看到楼上算法城管的帖子,十分地认同!

  36. 小城故事
    *.*.*.*
    链接

    小城故事 2010-01-07 21:28:00

    Jeffrey Zhao:

    小城故事:嘿嘿,比较了不少,还是偶的快速排序最好看


    嘿嘿,比Haskell的还好看?


    呵呵,这个就自愧不如了,偶是就知道C#的劳苦大众.Haskell优势在哪里,老赵觉得大家有必要了解下不?

  37. 老赵
    admin
    链接

    老赵 2010-01-07 21:36:00

    @小城故事
    Haskell就是最经典的函数式编程语言之一,是许多语言和开发方式的老师……我不知道是不是应该了解,我是用来开阔眼界的,也的确给了我不少启发。

  38. 卡通一下
    *.*.*.*
    链接

    卡通一下 2010-01-07 21:38:00

    算法城管:
    算法的最重要意义还是给一个解决问题的思路。
    而不是效率。


    好的算法不只是体现在代码的运行效率上,而是整个程序的运行方式。

    一个好的算法,可以影响整个程序的设计,从而提高程序的运行效率。

  39. 老赵
    admin
    链接

    老赵 2010-01-07 21:45:00

    @卡通一下
    其实这篇文章里我谈到的“算法”是指广义的“实现方式”,通俗地讲便是“代码的写法”。
    而“冒泡排序”等面向指定问题的解法,我把它称之为“经典算法”。在这里,我们还是加以区分吧。
    // 我已经在文章末尾补充这句话了,呵呵。

  40. 小城故事
    *.*.*.*
    链接

    小城故事 2010-01-07 21:58:00

    @Jeffrey Zhao
    我也觉得函数式编程语言至少应该有所了解, 那么从F#开始学怎么样呢?haskel可能比较古老,资料太少了

  41. fred chan
    *.*.*.*
    链接

    fred chan 2010-01-07 22:00:00

    期待接下来的两篇。
    长长见识。
    单单谈效果问题 ,会有口水战。

  42. 老赵
    admin
    链接

    老赵 2010-01-07 22:04:00

    @小城故事
    古老为什么等于资料少?正好相反吧,Haskell的资料绝对比F#多。

  43. 老赵
    admin
    链接

    老赵 2010-01-07 22:04:00

    @fred chan
    啥叫效果问题?

  44. fred chan
    *.*.*.*
    链接

    fred chan 2010-01-07 22:09:00

    约束条件 与效率 ,总是要权衡考虑的
    所以讨论就没有意义了
    但是我们需要知道 ,才能有权衡的能力
    关于你说的效率,我想你能给大家解释清楚。
    如果我的理解不对 ,请赐教。

  45. 卡通一下
    *.*.*.*
    链接

    卡通一下 2010-01-07 22:11:00

    楼主的这篇文章,似乎是说代码的“运行效率”更妥。

  46. fred chan
    *.*.*.*
    链接

    fred chan 2010-01-07 22:20:00

    感觉 ,我是泼冷水的 那个人。 惭愧了。

  47. 卡通一下
    *.*.*.*
    链接

    卡通一下 2010-01-07 22:29:00

    fred chan:感觉 ,我是泼冷水的 那个人。 惭愧了。


    还是题目有点问题。

    算法不能证明代码长与短的效率。

    我也有点晕...

  48. DiggingDeeply
    *.*.*.*
    链接

    DiggingDeeply 2010-01-07 22:40:00

    不能为了短而短。
    尺有所短,寸有所长

  49. 老赵
    admin
    链接

    老赵 2010-01-07 22:55:00

    卡通一下:楼主的这篇文章,似乎是说代码的“运行效率”更妥。


    我这篇文章还能理解出什么效率啊……

  50. 老赵
    admin
    链接

    老赵 2010-01-07 22:56:00

    卡通一下:
    还是题目有点问题。

    算法不能证明代码长与短的效率。

    我也有点晕...


    我用排序算法的例子,就是想说明代码长不一定效率差,代码短不一定效率好,好像没有什么问题吧?

  51. 老赵
    admin
    链接

    老赵 2010-01-07 22:57:00

    fred chan:
    约束条件 与效率 ,总是要权衡考虑的
    所以讨论就没有意义了
    但是我们需要知道 ,才能有权衡的能力
    关于你说的效率,我想你能给大家解释清楚。
    如果我的理解不对 ,请赐教。


    我没理解你说的东西……

  52. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-01-08 08:03:00

    Jeffrey Zhao:
    @温景良(Jason)
    写入们的东西啊?主要是不知道怎么写,容易变成翻译书。


    呵呵,这次写就知道怎么写了,您老再不出手,园子里没人写了,好多MVP出完书就没影了,博客n久更新一回

  53. 当耐特砖家
    *.*.*.*
    链接

    当耐特砖家 2010-01-08 08:50:00

    @温景良(Jason)
    特瑞李就是啊!

  54. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-01-08 09:45:00

    当耐特砖家:
    @温景良(Jason)
    特瑞李就是啊!


    这个我知道啊,上次出的那本书我有买啊,之后就不见影子了.

  55. skyaspnet
    *.*.*.*
    链接

    skyaspnet 2010-01-08 10:55:00

    算法还是最灵魂的

  56. 蛙蛙王子
    *.*.*.*
    链接

    蛙蛙王子 2010-01-29 23:08:00

    我没看懂那个冒泡排序,惭愧。。。。。

  57. 链接

    gaobanana 2010-04-10 11:23:43

    周末来看看这个系列,现在很多MVP就像 温景良(Jason) 哥们说的出本书就不更新博客了,感觉很不好,还是赵哥比较厉害,现在经常关顾,希望有更好的作品问世,什么时候也出本书呢?赵哥是在酝酿吗?出本像clr via c#这类经典的书!期待中……

  58. 老赵
    admin
    链接

    老赵 2010-04-10 12:05:28

    @gaobanana

    多谢支持,目前缺少好的题材和体裁,没法写出好书来,呵呵。

  59. ning
    122.228.135.*
    链接

    ning 2011-11-27 21:56:26

    快排函数里错了。

    public void QuickSort(int[] array)
    {
        QuickSortInternal(array, 0, array.Length);
    }
    

    array.Length 应为 array.Length-1

  60. fivemao
    180.171.226.*
    链接

    fivemao 2012-06-12 20:11:14

    比较两个算法的优劣是个很不容易的事情,首先要准确的估计运行时间。在 C 层面上我就没找到很好的办法去计算时间复杂度,更不用说 Haskell、OCaml、Ruby、Python 之类的“更高级”语言。事实上在汇编层面也只能做一个很粗糙的估计,就是将每一条指令的执行次数乘以他的指令周期数求和,像《The Art of Computer Programming》里面的算法分析那样。相对来说空间复杂度估计较容易点。其次得到了运行时间的表达式,还要对数据规模进行估计,才能确定优劣。例如两个排序算法,一个期望复杂度是 Anln(n) + Bn + O(log(n)),另外一个是 A'nln(n) + B'n + O(log(n)),一般来说是按照 (A,B) 和 (A',B') 的字典序比较,但若对实际的 n 的范围考量未必如此。此外,有的时候数据还有某种特性,往往在不同的概率分布下考虑复杂度,会显得困难重重。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我