理解Golang的Time结构

在 golang 中创建并打印一个时间对象,会看到如下输出 2018-10-26 14:15:50.306558969 +0800 CST m=+0.000401093 前面表示的意义好理解,分别是年月日和时间时区,最后的 m=+xxxx 这部分代表什么呢? Monotonic Clocks 和 Wall Clocks 根据 golang 的 time 包的文档可以知道,golang 的 time 结构中存储了两种时钟,一种是 Wall Clocks,一种是 Monotonic Clocks。 Wall Clocks,顾名思义,表示墙上挂的钟,在这里表示我们平时理解的时间,存储的形式是自 1970 年 1 月 1 日 0 时 0 分 0 秒以来的时间戳,当系统和授时服务器进行校准时间时间操作时,有可能造成这一秒是 2018-1-1 00:00:00,而下一秒变成了 2017-12-31 23:59:59 的情况。Monotonic Clocks,意思是单调时间的,所谓单调,就是只会不停的往前增长,不受校时操作的影响,这个时间是自进程启动以来的秒数。 如果每隔一秒生成一个 Time 并打印出来,就会看到如下输出。 2018-10-26 14:15:50.306558969 +0800 CST m=+0.000401093 2018-10-26 14:15:51.310559881 +0800 CST m=+1.004425285 2018-10-26 14:15:52.311822486 +0800 CST m=+2.005711106 2018-10-26 14:15:53.314599457 +0800 CST m=+3.008511329 2018-10-26 14:15:54.31882248 +0800 CST m=+4.012757636 2018-10-26 14:15:55.320059921 +0800 CST m=+5.014018292 2018-10-26 14:15:56.323814998 +0800 CST m=+6.017796644 2018-10-26 14:15:57.324858749 +0800 CST m=+7.018863606 2018-10-26 14:15:58.325164174 +0800 CST m=+8.019192224 2018-10-26 14:15:59.329058535 +0800 CST m=+9.023109863 2018-10-26 14:16:00.329591268 +0800 CST m=+10.023665796 可以看到 m=+后面所显示的数字,就是文档中所说的 Monotonic Clocks。 ...

十月 26, 2018 · 2 分钟 · Zhiya

翻译 | 更快的Python(二)

更快的 Python(Python Faster Way)使用代码示例来说明如何书写 Python 代码能带来更高的性能。本文对代码进行了讲解,从性能和可读性等角度来选择出最适合的写法。 例子 11:字符串连接 最差/最优时间比:1.15 使用建议:一次性连接多个(3 个以上)的字符串的时候,使用 join,其他情况使用加号或 f-string。 说明:又是一个字符串连接的问题,不过这个例子举的不好,join 适用的场景是一次连接多个字符串,会比加号连接多个字符串要快很多(加号相当于一个一个连接)。 例子 12:数字的格式化 最差/最优时间比:1.29 使用建议:需要复杂格式,推荐使用 format 方法;将数字转换为字符串,直接使用 str 方法。 说明:将数字转为字符串,使用 str 方法要快于 format 方法,因为 format 方法支持在转换过程中增加规则,例如将数字转为货币形式(每三位加一个逗号分隔符)。 例子 13:获取内置列表类型的长度 最差/最优时间比:1.20 使用建议:使用 len()方法。 说明:当调用 len()方法时,系统实际上是调用了对象内置的len方法,从这个层面理解,直接调用len应该比 len()方法更快。但是当 len()内置的列表方法时,Python 解释器做了优化,直接返回了列表对象中存储长度信息的变量,并不会调用len。 例子 14:整数类型的运算 最差/最优时间比:2.63 使用建议:不要直接调用add等魔术方法。 说明:对于整数类型,调用魔术方法完成运算的速度远远慢于直接使用运算符,使用运算符时,Python 解释器直接调用 C 实现的 operaotr 包中的运算方法,所以速度很快;而使用调用魔术方法,在 Python 层面多出了调用add等魔术方法的额外操作。 例子 15:自定义类型的运算符重载 最差/最优时间比:1.06 使用建议:不要直接调用add等魔术方法。 说明:对于重载了运算符的对象,没有对应的 C 实现运算方法,所以直接直接调用魔术方法速度会更快。 例子 16:对 range 结果求和 ...

十月 25, 2018 · 1 分钟 · Zhiya

Go的栈空间管理

栈空间管理的基本逻辑 go 语言通过 goroutine 提供了并发编程支持,goroutine 是 go 运行库的功能,而不是操作系统线程实现的,goroutine 可以被理解成一个用户态的线程。 既然 goroutine 是由 go 运行库管理的,那么 go 运行库也需要为每个 goroutine 创建并管理相应的栈空间,为每个 goroutine 分配的栈空间不能太大,goroutine 开多时会浪费大量空间,也不能太小,会导致栈溢出。go 语言选择栈的栈空间管理的方式是,一开始给一个比较小的空间,随着需要自动增长。当 goroutine 不需要那么大的空间时,栈空间也要自动缩小。 分段栈 Segment Stacks 在 go 1.3 之前,go 使用分段栈。 分段栈实现了一种不连续但是可以持续增长的栈,开始时,栈只有一个段,当需要更多的栈空间时,会分配一个新的段,和上一个栈双向链接。这样,一个栈就是由多个双向链接的段所组成的。当新分配的段使用完毕后,新段会被释放掉。 分段栈实现了栈的按需收缩,在增加新分段时也不需要对原有分段中的数据进行拷贝,使得 goroutine 的使用代价非常低廉。 分段栈的好处是可以按需增长,空间利用率比较高,然而分段栈在某些情况下也存在一定的瑕疵。当一个段即将用尽,这时使用 for 循环执行一个比较耗空间的函数,会导致函数执行时 goroutine 进行段的分配,而执行完成返回时,进行段的销毁,这样就会导致在循环中出现多次栈的扩容和收缩,造成很大的性能损失,这种情况被称作栈分裂(Stack Split)。 连续栈 Contiguous Stacks go 1.3 推出了连续栈,连续栈使用了另外一种策略,不再把栈分成一段一段的,当栈空间不够时,直接 new 一个 2 倍大的栈空间,并将原先栈空间中的数据拷贝到新的栈空间中,而后销毁旧栈。这样当出现栈空间触及边界时,不会产生栈分裂的情况。 继续假设当前栈空间即将用尽,并且需要在 for 循环中执行一个比较消耗空间的函数。当该函数执行时,栈空间发生了扩容,变成原先 2 倍大小,函数执行完成一次后,栈空间的使用量缩小回执行前的大小,但是栈空间的使用量并没有小于栈大小的 1/4,不会触发栈收缩,所以在整个 for 循环执行过程中,不会反复触发栈空间的收缩扩容。 总结 相比于分段栈,连续栈避免了某些场景下栈空间的的频繁伸缩。有一点需要注意的是,连续栈的收缩也是需要重新申请一段空间(原先的 1/2 大小),并进行栈拷贝操作的。

十月 11, 2018 · 1 分钟 · Zhiya

翻译 | 更快的Python(一)

更快的 Python(Python Faster Way)使用代码示例来说明如何书写 Python 代码能带来更高的性能。本文对代码进行了讲解,从性能和可读性等角度来选择出最适合的写法。 例子 1:字符串格式化 最差/最优时间比:1.95 使用建议:Python 3.7 或以上推荐使用 f-string,其他版本推荐使用 format 方法。 说明:字符串格式化是代码中最常遇到的情况,虽然在连接少量字符串的情景中,使用+号的性能最优,但是使用+号的代码可读性最差。如果使用 Python 3.7 或优以上版本,可以使用 f-string 来解决这个问题,f-string 的性能比 format 方法和%操作符的性能都要高,可读性也比+号好。 例子 2:字典的初始化 最差/最优时间比:1.83 使用建议:使用字面量初始化字典(以及其他集合类型)。 说明:Python 中初始化集合类型时使用字面量的方式,解释器会直接调用 BUILD_MAP 等字节码来创建,如果用构造函数的方式来创建,则需要先查询构造方法,再执行构造方法。使用字面量初始化,Python 代码也更简洁。 例子 3:内置排序方法 最差/最优时间比:1.26 使用建议:根据是否需要修改原始值来决定使用哪个方法。 说明:sorted 和 list.sort 方法是 Python 中内置的排序方法,sorted 方法不会修改原始值,list.sort 方法在原始值上直接排序,会修改原始值。比较这两个方法的性能差异,意义不大。 例子 4:初始化多个变量 最差/最优时间比:1.01 使用建议:推荐使用第二种。 说明:从字节码中可以看出两种方式出了执行顺序之外,基本一致,所以性能上也非常接近。 例子 5:多个变量的比较 最差/最优时间比:1.11 使用建议:推荐使用第二种。 说明:使用第一种方法能带来一定的性能提升,但是提升有限,在实际情况中也很少出现多个变量连续比较大小的情况,并且第一种方法非常不 Pythonic,所以推荐使用第二种。 例子 6:if true 的条件判断 最差/最优时间比:1.17 使用建议:推荐使用第一种。 说明:从字节码上看,第一种方法的性能最高,并且语法上也更加简洁。 例子 7:if false 的条件判断 ...

十月 8, 2018 · 1 分钟 · Zhiya

Go语言中defer的一些坑

defer 语句是 Go 中一个非常有用的特性,可以将一个方法延迟到包裹该方法的方法返回时执行,在实际应用中,defer 语句可以充当其他语言中 try…catch…的角色,也可以用来处理关闭文件句柄等收尾操作。 defer 触发时机 A “defer” statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking. Go 官方文档中对 defer 的执行时机做了阐述,分别是。 包裹 defer 的函数返回时 包裹 defer 的函数执行到末尾时 所在的 goroutine 发生 panic 时 defer 执行顺序 当一个方法中有多个 defer 时, defer 会将要延迟执行的方法“压栈”,当 defer 被触发时,将所有“压栈”的方法“出栈”并执行。所以 defer 的执行顺序是 LIFO 的。 ...

九月 14, 2018 · 2 分钟 · Zhiya