2020年5月

上一篇文章中我论述了 叶子内联 leaf inlining 是怎样让 Go 编译器减少函数调用的开销的,以及延伸出了跨函数边界的优化的机会。本文中,我要论述内联的限制以及叶子内联与 栈中内联 mid-stack inlining 的对比。

内联的限制

把函数内联到它的调用处消除了调用的开销,为编译器进行其他的优化提供了更好的机会,那么问题来了,既然内联这么好,内联得越多开销就越少,为什么不尽可能多地内联呢?

内联可能会以增加程序大小换来更快的执行时间。限制内联的最主要原因是,创建许多函数的内联副本会增加编译时间,并导致生成更大的二进制文件的边际效应。即使把内联带来的进一步的优化机会考虑在内,太激进的内联也可能会增加生成的二进制文件的大小和编译时间。

内联收益最大的是小函数,相对于调用它们的开销来说,这些函数做很少的工作。随着函数大小的增长,函数内部做的工作与函数调用的开销相比省下的时间越来越少。函数越大通常越复杂,因此优化其内联形式相对于原地优化的好处会减少。

内联预算

在编译过程中,每个函数的内联能力是用内联预算计算的 1 。开销的计算过程可以巧妙地内化,像一元和二元等简单操作,在 抽象语法数 Abstract Syntax Tree (AST)中通常是每个节点一个单位,更复杂的操作如 make 可能单位更多。考虑下面的例子:

package main

func small() string {
    s := "hello, " + "world!"
    return s
}

func large() string {
    s := "a"
    s += "b"
    s += "c"
    s += "d"
    s += "e"
    s += "f"
    s += "g"
    s += "h"
    s += "i"
    s += "j"
    s += "k"
    s += "l"
    s += "m"
    s += "n"
    s += "o"
    s += "p"
    s += "q"
    s += "r"
    s += "s"
    s += "t"
    s += "u"
    s += "v"
    s += "w"
    s += "x"
    s += "y"
    s += "z"
    return s
}

func main() {
    small()
    large()
}

使用 -gcflags=-m=2 参数编译这个函数能让我们看到编译器分配给每个函数的开销:

% go build -gcflags=-m=2 inl.go
# command-line-arguments
./inl.go:3:6: can inline small with cost 7 as: func() string { s := "hello, world!"; return s }
./inl.go:8:6: cannot inline large: function too complex: cost 82 exceeds budget 80
./inl.go:38:6: can inline main with cost 68 as: func() { small(); large() }
./inl.go:39:7: inlining call to small func() string { s := "hello, world!"; return s }

编译器根据函数 func small() 的开销(7)决定可以对它内联,而 func large() 的开销太大,编译器决定不进行内联。func main() 被标记为适合内联的,分配了 68 的开销;其中 small 占用 7,调用 small 函数占用 57,剩余的(4)是它自己的开销。

可以用 -gcflag=-l 参数控制内联预算的等级。下面是可使用的值:

  • -gcflags=-l=0 默认的内联等级。
  • -gcflags=-l(或 -gcflags=-l=1)取消内联。
  • -gcflags=-l=2-gcflags=-l=3 现在已经不使用了。和 -gcflags=-l=0 相比没有区别。
  • -gcflags=-l=4 减少非叶子函数和通过接口调用的函数的开销。 2

不确定语句的优化

一些函数虽然内联的开销很小,但由于太复杂它们仍不适合进行内联。这就是函数的不确定性,因为一些操作的语义在内联后很难去推导,如 recoverbreak。其他的操作,如 selectgo 涉及运行时的协调,因此内联后引入的额外的开销不能抵消内联带来的收益。

不确定的语句也包括 forrange,这些语句不一定开销很大,但目前为止还没有对它们进行优化。

栈中函数优化

在过去,Go 编译器只对叶子函数进行内联 —— 只有那些不调用其他函数的函数才有资格。在上一段不确定的语句的探讨内容中,一次函数调用就会让这个函数失去内联的资格。

进入栈中进行内联,就像它的名字一样,能内联在函数调用栈中间的函数,不需要先让它下面的所有的函数都被标记为有资格内联的。栈中内联是 David Lazar 在 Go 1.9 中引入的,并在随后的版本中做了改进。这篇文稿深入探究了保留栈追踪行为和被深度内联后的代码路径里的 runtime.Callers 的难点。

在前面的例子中我们看到了栈中函数内联。内联后,func main() 包含了 func small() 的函数体和对 func large() 的一次调用,因此它被判定为非叶子函数。在过去,这会阻止它被继续内联,虽然它的联合开销小于内联预算。

栈中内联的最主要的应用案例就是减少贯穿函数调用栈的开销。考虑下面的例子:

package main

import (
    "fmt"
    "strconv"
)

type Rectangle struct {}

//go:noinline
func (r *Rectangle) Height() int {
    h, _ := strconv.ParseInt("7", 10, 0)
    return int(h)
}

func (r *Rectangle) Width() int {
    return 6
}

func (r *Rectangle) Area() int { return r.Height() * r.Width() }

func main() {
    var r Rectangle
    fmt.Println(r.Area())
}

在这个例子中, r.Area() 是个简单的函数,调用了两个函数。r.Width() 可以被内联,r.Height() 这里用 //go:noinline 指令标注了,不能被内联。 3

% go build -gcflags='-m=2' square.go                                                                                                          
# command-line-arguments
./square.go:12:6: cannot inline (*Rectangle).Height: marked go:noinline                                                                               
./square.go:17:6: can inline (*Rectangle).Width with cost 2 as: method(*Rectangle) func() int { return 6 }
./square.go:21:6: can inline (*Rectangle).Area with cost 67 as: method(*Rectangle) func() int { return r.Height() * r.Width() }                       
./square.go:21:61: inlining call to (*Rectangle).Width method(*Rectangle) func() int { return 6 }                                                     
./square.go:23:6: cannot inline main: function too complex: cost 150 exceeds budget 80                        
./square.go:25:20: inlining call to (*Rectangle).Area method(*Rectangle) func() int { return r.Height() * r.Width() }
./square.go:25:20: inlining call to (*Rectangle).Width method(*Rectangle) func() int { return 6 }

由于 r.Area() 中的乘法与调用它的开销相比并不大,因此内联它的表达式是纯收益,即使它的调用的下游 r.Height() 仍是没有内联资格的。

快速路径内联

关于栈中内联的效果最令人吃惊的例子是 2019 年 Carlo Alberto Ferraris 通过允许把 sync.Mutex.Lock() 的快速路径(非竞争的情况)内联到它的调用方来提升它的性能。在这个修改之前,sync.Mutex.Lock() 是个很大的函数,包含很多难以理解的条件,使得它没有资格被内联。即使锁可用时,调用者也要付出调用 sync.Mutex.Lock() 的代价。

Carlo 把 sync.Mutex.Lock() 分成了两个函数(他自己称为 外联 outlining )。外部的 sync.Mutex.Lock() 方法现在调用 sync/atomic.CompareAndSwapInt32() 且如果 CAS( 比较并交换 Compare and Swap )成功了之后立即返回给调用者。如果 CAS 失败,函数会走到 sync.Mutex.lockSlow() 慢速路径,需要对锁进行注册,暂停 goroutine。 4

% go build -gcflags='-m=2 -l=0' sync 2>&1 | grep '(*Mutex).Lock'
../go/src/sync/mutex.go:72:6: can inline (*Mutex).Lock with cost 69 as: method(*Mutex) func() { if "sync/atomic".CompareAndSwapInt32(&m.state, 0, mutexLocked) { if race.Enabled {  }; return  }; m.lockSlow() }

通过把函数分割成一个简单的不能再被分割的外部函数,和(如果没走到外部函数就走到的)一个处理慢速路径的复杂的内部函数,Carlo 组合了栈中函数内联和编译器对基础操作的支持,减少了非竞争锁 14% 的开销。之后他在 sync.RWMutex.Unlock() 重复这个技巧,节省了另外 9% 的开销。

相关文章:

  1. Go 中的内联优化
  2. goroutine 的栈为什么会无限增长?
  3. 栈追踪和 errors 包
  4. 零值是什么,为什么它很有用?

  1. 不同发布版本中,在考虑该函数是否适合内联时,Go 编译器对同一函数的预算是不同的。
  2. 时刻记着编译器的作者警告过“更高的内联等级(比 -l 更高)可能导致错误或不被支持”。 Caveat emptor。
  3. 编译器有足够的能力来内联像 strconv.ParseInt 的复杂函数。作为一个实验,你可以尝试去掉 //go:noinline 注释,使用 -gcflags=-m=2 编译后观察。
  4. race.Enable 表达式是通过传递给 go 工具的 -race 参数控制的一个常量。对于普通编译,它的值是 false,此时编译器可以完全省略代码路径。

via: https://dave.cheney.net/2020/05/02/mid-stack-inlining-in-go

作者:Dave Cheney 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

以下是安装 Ubuntu 20.04 之后需要做的一些调整和事项,它将使你获得更流畅、更好的桌面 Linux 体验。

Ubuntu 20.04 LTS(长期支持版)带来了许多新的特性和观感上的变化。如果你要安装 Ubuntu 20.04,让我向你展示一些推荐步骤便于你的使用。

安装完 Ubuntu 20.04 LTS “Focal Fossa” 后要做的 16 件事

我在这里提到的步骤仅是我的建议。如果一些定制或调整不适合你的需要和兴趣,你可以忽略它们。

同样的,有些步骤看起来很简单,但是对于一个 Ubuntu 新手来说是必要的。

这里的一些建议适用于启用 GNOME 作为默认桌面 Ubuntu 20.04,所以请检查 Ubuntu 版本桌面环境

以下列表便是安装了代号为 Focal Fossa 的 Ubuntu 20.04 LTS 之后要做的事。

1、通过更新和启用额外的软件仓库来准备你的系统

安装 Ubuntu 或任何其他 Linux 发行版之后,你应该做的第一件事就是更新它。Linux 的运作是建立在本地的可用软件包数据库上,而这个缓存需要同步以便你能够安装软件。

升级 Ubuntu 非常简单。你可以运行软件更新从菜单(按 Super 键并搜索 “software updater”):

Ubuntu 20.04 的软件升级器

你也可以在终端使用以下命令更新你的系统:

sudo apt update && sudo apt upgrade

接下来,你应该确保启用了 universe(宇宙)和 multiverse(多元宇宙)软件仓库。使用这些软件仓库,你可以访问更多的软件。我还推荐阅读关于 Ubuntu 软件仓库的文章,以了解它背后的基本概念。

在菜单中搜索 “Software & Updates”:

软件及更新设置项

请务必选中软件仓库前面的勾选框:

启用额外的软件仓库

2、安装媒体解码器来播放 MP3、MPEG4 和其他格式媒体文件

如果你想播放媒体文件,如 MP3、MPEG4、AVI 等,你需要安装媒体解码器。由于各个国家的版权问题, Ubuntu 在默认情况下不会安装它。

作为个人,你可以使用 Ubuntu Restricted Extra 安装包很轻松地安装这些媒体编解码器。这将在你的 Ubuntu 系统安装媒体编解码器、Adobe Flash 播放器和微软 True Type 字体等。

你可以通过点击这个链接来安装它(它会要求在软件中心打开它),或者使用以下命令:

sudo apt install ubuntu-restricted-extras

如果遇到 EULA 或许可证界面,请记住使用 tab 键在选项之间进行选择,然后按回车键确认你的选择。

按 tab 键选择 OK 并按回车键

3、从软件中心或网络上安装软件

现在已经设置好了软件仓库并更新了软件包缓存,应该开始安装所需的软件了。

在 Ubuntu 中安装应用程序有几种方法,最简单和正式的方法是使用软件中心。

Ubuntu 软件中心

如果你想要一些关于软件的建议,请参考这个丰富的各种用途的 Ubuntu 应用程序列表

一些软件供应商提供了 .deb 文件来方便地安装他们的应用程序。你可以从他们的网站获得 .deb 文件。例如,要在 Ubuntu 上安装谷歌 Chrome,你可以从它的网站上获得 .deb 文件,双击它开始安装。

4、享受 Steam Proton 和 GameModeEnjoy 上的游戏

在 Linux 上进行游戏已经有了长足的发展。你不再受限于自带的少数游戏。你可以在 Ubuntu 上安装 Steam并享受许多游戏。

Steam 新的 Proton 项目可以让你在 Linux 上玩许多只适用于 Windows 的游戏。除此之外,Ubuntu 20.04 还默认安装了 Feral Interactive 的 GameMode

GameMode 会自动调整 Linux 系统的性能,使游戏具有比其他后台进程更高的优先级。

这意味着一些支持 GameMode 的游戏(如古墓丽影·崛起)在 Ubuntu 上的性能应该有所提高。

5、管理自动更新(适用于进阶用户和专家)

最近,Ubuntu 已经开始自动下载并安装对你的系统至关重要的安全更新。这是一个安全功能,作为一个普通用户,你应该让它保持默认开启。

但是,如果你喜欢自己进行配置更新,而这个自动更新经常导致你“无法锁定管理目录”错误,也许你可以改变自动更新行为。

你可以选择“立即显示”,这样一有安全更新就会立即通知你,而不是自动安装。

管理自动更新设置

6、控制电脑的自动挂起和屏幕锁定

如果你在笔记本电脑上使用 Ubuntu 20.04,那么你可能需要注意一些电源和屏幕锁定设置。

如果你的笔记本电脑处于电池模式,Ubuntu 会在 20 分钟不活动后休眠系统。这样做是为了节省电池电量。就我个人而言,我不喜欢它,因此我禁用了它。

类似地,如果你离开系统几分钟,它会自动锁定屏幕。我也不喜欢这种行为,所以我宁愿禁用它。

Ubuntu 20.04 的电源设置

7、享受夜间模式

Ubuntu 20.04 中最受关注的特性之一是夜间模式。你可以通过进入设置并在外观部分中选择它来启用夜间模式。

开启夜间主题 Ubuntu

你可能需要做一些额外的调整来获得完整的 Ubuntu 20.04 夜间模式

8、控制桌面图标和启动程序

如果你想要一个最简的桌面,你可以禁用桌面上的图标。你还可以从左侧禁用启动程序,并在顶部面板中禁用软件状态栏。

所有这些都可以通过默认的新 GNOME 扩展来控制,该程序默认情况下已经可用。

禁用 Ubuntu 20 04 的 Dock

顺便说一下,你也可以通过“设置”->“外观”来将启动栏的位置改变到底部或者右边。

9、使用表情符和特殊字符,或从搜索中禁用它

Ubuntu 提供了一个使用表情符号的简单方法。在默认情况下,有一个专用的应用程序叫做“字符”。它基本上可以为你提供表情符号的 Unicode

不仅是表情符号,你还可以使用它来获得法语、德语、俄语和拉丁语字符的 unicode。单击符号你可以复制 unicode,当你粘贴该代码时,你所选择的符号便被插入。

Ubuntu 表情符

你也能在桌面搜索中找到这些特殊的字符和表情符号。也可以从搜索结果中复制它们。

表情符出现在桌面搜索中

如果你不想在搜索结果中看到它们,你应该禁用搜索功能对它们的访问。下一节将讨论如何做到这一点。

10、掌握桌面搜索

GNOME 桌面拥有强大的搜索功能,大多数人使用它来搜索已安装的应用程序,但它不仅限于此。

Super 键并搜索一些东西,它将显示与搜索词匹配的任何应用程序,然后是系统设置和软件中心提供的匹配应用程序。

桌面搜索

不仅如此,搜索还可以找到文件中的文本。如果你正在使用日历,它也可以找到你的会议和提醒。你甚至可以在搜索中进行快速计算并复制其结果。

Ubuntu搜索的快速计算

你可以进入“设置”中来控制可以搜索的内容和顺序。

11、使用夜灯功能,减少夜间眼睛疲劳

如果你在晚上使用电脑或智能手机,你应该使用夜灯功能来减少眼睛疲劳。我觉得这很有帮助。

夜灯的特点是在屏幕上增加了一种黄色的色调,比白光少了一些挤压感。

你可以在“设置”->“显示”切换到夜灯选项卡来开启夜光功能。你可以根据自己的喜好设置“黄度”。

夜灯功能

12、使用 2K/4K 显示器?使用分辨率缩放得到更大的图标和字体

如果你觉得图标、字体、文件夹在你的高分辨率屏幕上看起来都太小了,你可以利用分辨率缩放。

启用分辨率缩放可以让你有更多的选项来从 100% 增加到 200%。你可以选择适合自己喜好的缩放尺寸。

在设置->显示中启用高分缩放

13、探索 GNOME 扩展功能以扩展 GNOME 桌面可用性

GNOME 桌面有称为“扩展”的小插件或附加组件。你应该学会使用 GNOME 扩展来扩展系统的可用性。

如下图所示,天气扩展顶部面板中显示了天气信息。不起眼但十分有用。你也可以在这里查看一些最佳 GNOME 扩展。不需要全部安装,只使用那些对你有用的。

天气扩展

14、启用“勿扰”模式,专注于工作

如果你想专注于工作,禁用桌面通知会很方便。你可以轻松地启用“勿扰”模式,并静音所有通知。

启用“请勿打扰”清除桌面通知

这些通知仍然会在消息栏中,以便你以后可以阅读它们,但是它们不会在桌面上弹出。

15、清理你的系统

这是你安装 Ubuntu 后不需要马上做的事情。但是记住它会对你有帮助。

随着时间的推移,你的系统将有大量不再需要的包。你可以用这个命令一次性删除它们:

sudo apt autoremove

还有其他清理 Ubuntu 以释放磁盘空间的方法,但这是最简单和最安全的。

16、根据你的喜好调整和定制 GNOME 桌面

我强烈推荐安装 GNOME 设置工具。这将让你可以通过额外的设置来进行定制。

Gnome 设置工具

比如,你可以以百分比形式显示电池容量修正在触摸板右键问题、改变 Shell 主题、改变鼠标指针速度、显示日期和星期数、改变应用程序窗口行为等。

定制是没有尽头的,我可能仅使用了它的一小部分功能。这就是为什么我推荐阅读这些关于自定义 GNOME 桌面的文章。

你也可以在 Ubuntu 中安装新主题,不过就我个人而言,我喜欢这个版本的默认主题。这是我第一次在 Ubuntu 发行版中使用默认的图标和主题。

安装 Ubuntu 之后你会做什么?

如果你是 Ubuntu 的初学者,我建议你阅读这一系列 Ubuntu 教程开始学习。

这就是我的建议。安装 Ubuntu 之后你要做什么?分享你最喜欢的东西,我可能根据你的建议来更新这篇文章。


via: https://itsfoss.com/things-to-do-after-installing-ubuntu-20-04/

作者:Abhishek Prakash 选题:lujun9972 译者:qfzy1233 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

单用户模式,也被称为维护模式,超级用户可以在此模式下恢复/修复系统问题。

通常情况下,这类问题在多用户环境中修复不了。系统可以启动但功能不能正常运行或者你登录不了系统。

在基于 Red Hat(RHEL)7/8 的系统中,使用 runlevel1.targetrescue.target 来实现。

在此模式下,系统会挂载所有的本地文件系统,但不开启网络接口。

系统仅启动特定的几个服务和修复系统必要的尽可能少的功能。

当你想运行文件系统一致性检查来修复损坏的文件系统,或忘记 root 密码后重置密码,或要修复系统上的一个挂载点问题时,这个方法会很有用。

你可以用下面三种方法以单用户模式启动 CentOS/RHEL 7/8 系统。

  • 方法 1:通过向内核添加 rd.break 参数来以单用户模式启动 CentOS/RHEL 7/8 系统
  • 方法 2:通过用 init=/bin/bashinit=/bin/sh 替换内核中的 rhgb quiet 语句来以单用户模式启动 CentOS/RHEL 7/8 系统
  • 方法 3:通过用 rw init=/sysroot/bin/sh 参数替换内核中的 ro 语句以单用户模式启动 CentOS/RHEL 7/8 系统

方法 1

通过向内核添加 rd.break 参数来以单用户模式启动 CentOS/RHEL 7/8 系统。

重启你的系统,在 GRUB2 启动界面,按下 e 键来编辑选中的内核。你需要选中第一行,第一个是最新的内核,然而如果你想用旧的内核启动系统你也可以选择其他的行。

根据你的 RHEL/CentOS 版本,找到 linux16linux 语句,按下键盘上的 End 键,跳到行末,像下面截图中展示的那样添加关键词 rd.break,按下 Ctrl+xF10 来进入单用户模式。

如果你的系统是 RHEL/CentOS 7,你需要找 linux16,如果你的系统是 RHEL/CentOS 8,那么你需要找 linux

这个修改会让你的 root 文件系统以 “只读(ro)” 模式挂载。你可以用下面的命令来验证下。下面的输出也明确地告诉你当前是在 “ 紧急模式 Emergency Mode ”。

# mount | grep root

为了修改 sysroot 文件系统,你需要用读写模式(rw)重新挂载它。

# mount -o remount,rw /sysroot

运行下面的命令修改环境,这就是大家熟知的 “监禁目录” 或 “chroot 监狱”。

# chroot /sysroot

现在,单用户模式已经完全准备好了。当你修复了你的问题要退出单用户模式时,执行下面的步骤。

CentOS/RHEL 7/8 默认使用 SELinux,因此创建下面的隐藏文件,这个文件会在下一次启动时重新标记所有文件。

# touch /.autorelabel

最后,用下面的命令重启系统。你也可以输入两次 exit 命令来重启你的系统。

# reboot -f

方法 2

通过用 init=/bin/bashinit=/bin/sh 替换内核中的 rhgb quiet 语句来以单用户模式启动 CentOS/RHEL 7/8 系统。

重启你的系统,在 GRUB2 启动界面,按下 e 键来编辑选中的内核。

找到语句 rhgb quiet,用 init=/bin/bashinit=/bin/sh 替换它,然后按下 Ctrl+xF10 来进入单用户模式。

init=/bin/bash 的截图。

init=/bin/sh 的截图。

默认情况下,上面的操作会以只读(ro)模式挂载你的 / 分区,因此你需要以读写(rw)模式重新挂载 / 文件系统,这样才能修改它。

# mount -o remount,rw /

现在你可以执行你的任务了。当结束时,执行下面的命令来开启重启时的 SELinux 重新标记。

# touch /.autorelabel

最后,重启系统。

# exec /sbin/init 6

方法 3

通过用 rw init=/sysroot/bin/sh 参数替换内核中的 ro 单词,以单用户模式启动 CentOS/RHEL 7/8 系统。

为了中断自动启动的过程,重启你的系统并在 GRUB2 启动界面按下任意键。

现在会展示你系统上所有可用的内核,选择最新的内核,按下 e 键来编辑选中的内核参数。

找到以 linuxlinux16 开头的语句,用 rw init=/sysroot/bin/sh 替换 ro。替换完后按下 Ctrl+xF10 来进入单用户模式。

运行下面的命令把环境切换为 “chroot 监狱”。

# chroot /sysroot

如果需要,做出必要的修改。修改完后,执行下面的命令来开启重启时的 SELinux 重新标记。

# touch /.autorelabel

最后,重启系统。

# reboot -f

via: https://www.2daygeek.com/boot-centos-7-8-rhel-7-8-single-user-mode/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Git Extras 版本库包含了 60 多个脚本,它们是 Git 基本功能的补充。以下是如何安装、使用和贡献的方法。

2005 年,Linus Torvalds 创建了 Git,以取代他之前用于维护 Linux 内核的分布式源码控制管理的专有解决方案。从那时起,Git 已经成为开源和云原生开发团队的主流版本控制解决方案。

但即使是像 Git 这样功能丰富的应用程序,也没有人们想要或需要的每个功能,所以会有人花大力气去创建这些缺少的功能。就 Git 而言,这个人就是 TJ Holowaychuk。他的 Git Extras 项目承载了 60 多个“附加功能”,这些功能扩展了 Git 的基本功能。

使用 Git 附加功能

下面介绍一下如何使用四种最受欢迎的 Git 附加功能。

git-ignore

git ignore 是一个方便的附加功能,它可以让你手动添加文件类型和注释到 .git-ignore 文件中,而不需要打开文本编辑器。它可以操作你的个人用户帐户的全局忽略文件和单独用于你正在工作的版本库中的忽略文件。

在不提供参数的情况下执行 git ignore 会先列出全局忽略文件,然后是本地的忽略文件。

$ git ignore
Global gitignore: /home/alice/.gitignore
# Numerous always-ignore extensions
*.diff
*.err
*.orig
*.rej
*.swo
*.swp
*.vi
*~
*.sass-cache

# OS or Editor folders
Thumbs.db
---------------------------------
Local gitignore: .gitignore
nbproject

git-info

git info 可以检索你所需要的所有信息,以获取你正在使用的版本库的上下文信息。它包括远程 URL、远程分支、本地分支、配置信息和最后一次的提交信息。

$ git info

## Remote URLs:

origin      [email protected]:sampleAuthor/git-extras.git (fetch)
origin      [email protected]:sampleAuthor/git-extras.git (push)

## Remote Branches:

origin/HEAD -> origin/master
origin/myBranch

## Local Branches:

myBranch
* master

## Most Recent Commit:

commit e3952df2c172c6f3eb533d8d0b1a6c77250769a7
Author: Sample Author <[email protected]>

Added git-info command.

Type ´git log´ for more commits, or ´git show <commit id>´ for full commit details.

## Configuration (.git/config):

color.diff=auto
color.status=auto
color.branch=auto
user.name=Sample Author
[email protected]
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
[email protected]:mub/git-extras.git
branch.master.remote=origin
branch.master.merge=refs/heads/master

git-mr 和 git-pr

这些附加功能的作用类似,工作方式也基本相同。

  • git mr 检出来自 GitLab 的合并请求。
  • git pr 检出来自 GitHub 的拉取请求。

无论是哪种情况,你只需要合并请求号/拉取请求号或完整的 URL,它就会抓取远程引用,检出分支,并调整配置,这样 Git 就知道要替换哪个分支了。

$ git mr 51
From gitlab.com:owner/repository
 * [new ref]         refs/merge-requests/51/head -> mr/51
Switched to branch 'mr/51'

git-release

通过将 committagpush 合并到一个命令中,git release 可以节省大量的按键来执行这三个命令,而这三个命令往往是依次运行的。

要用特定的 <tagname> 和自定义消息提交:

$ git release 0.1.0 -m <+ powerful feature added>

其他附加功能

这只是该版本库中 60 多个 Git 附加功能中的四个命令。要访问 Git Extras 中的全部命令,请查看该源代码库中的 Commands.md 文件,或者在安装 Git Extras 后运行以下命令。

$ git extras --help

安装 Git 附加功能

使用 Git 附加功能的主要前提是安装了 Git 的命令行版本。如果你打算从源码中构建,还需要有额外的工具(例如:make)。

如果你使用的是最新版本的 macOS,那么 Git 附加功能的安装最好使用 Homebrew(和大多数开源工具一样)。

$ brew install git-extras

在 Linux 上,每个平台原生的包管理器中都包含有 Git Extras。有时,你需要启用额外的仓库,比如在 CentOS 上的 EPEL,然后运行一条命令。

$ sudo yum install git-extras

其他 Linux 发行版、BSD 和其他平台的完整安装说明可以在该版本库的 Installation.md 文件中找到。

贡献

你是否认为 Git 中有缺少的功能,并且已经构建了一个脚本来处理它?为什么不把它作为 Git Extras 发布版的一部分,与全世界分享呢?

要做到这一点,请将该功能贡献到 Git Extras 仓库中。更多具体细节请参见仓库中的 CONTRIBUTING.md 文件,但基本的操作方法很简单:

  1. 创建一个处理该功能的 Bash 脚本。
  2. 创建一个基本的 man 文件,让大家知道如何使用它。
  3. 更新命令列表和补完脚本,让人们知道这个功能的存在。
  4. 运行完整性检查,确保你没有破坏任何东西。
  5. 为你的功能创建一个拉取请求。

向 Git Extras 贡献贡献,会让你的 Git 用户的生活更轻松一些。你可以在项目的 README 中了解更多。


via: https://opensource.com/article/20/4/git-extras

作者:Vince Power 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文讨论 Go 编译器是如何实现内联的,以及这种优化方法如何影响你的 Go 代码。

请注意:本文重点讨论 gc,这是来自 golang.org 的事实标准的 Go 编译器。讨论到的概念可以广泛适用于其它 Go 编译器,如 gccgo 和 llgo,但它们在实现方式和功效上可能有所差异。

内联是什么?

内联 inlining 就是把简短的函数在调用它的地方展开。在计算机发展历程的早期,这个优化是由程序员手动实现的。现在,内联已经成为编译过程中自动实现的基本优化过程的其中一步。

为什么内联很重要?

有两个原因。第一个是它消除了函数调用本身的开销。第二个是它使得编译器能更高效地执行其他的优化策略。

函数调用的开销

在任何语言中,调用一个函数 1 都会有消耗。把参数编组进寄存器或放入栈中(取决于 ABI),在返回结果时的逆反过程都会有开销。引入一次函数调用会导致程序计数器从指令流的一点跳到另一点,这可能导致管道滞后。函数内部通常有 前置处理 preamble ,需要为函数执行准备新的栈帧,还有与前置相似的 后续处理 epilogue ,需要在返回给调用方之前释放栈帧空间。

在 Go 中函数调用会消耗额外的资源来支持栈的动态增长。在进入函数时,goroutine 可用的栈空间与函数需要的空间大小进行比较。如果可用空间不同,前置处理就会跳到 运行时 runtime 的逻辑中,通过把数据复制到一块新的、更大的空间的来增长栈空间。当这个复制完成后,运行时就会跳回到原来的函数入口,再执行栈空间检查,现在通过了检查,函数调用继续执行。这种方式下,goroutine 开始时可以申请很小的栈空间,在有需要时再申请更大的空间。 2

这个检查消耗很小,只有几个指令,而且由于 goroutine 的栈是成几何级数增长的,因此这个检查很少失败。这样,现代处理器的分支预测单元可以通过假定检查肯定会成功来隐藏栈空间检查的消耗。当处理器预测错了栈空间检查,不得不放弃它在推测性执行所做的操作时,与为了增加 goroutine 的栈空间运行时所需的操作消耗的资源相比,管道滞后的代价更小。

虽然现代处理器可以用预测性执行技术优化每次函数调用中的泛型和 Go 特定的元素的开销,但那些开销不能被完全消除,因此在每次函数调用执行必要的工作过程中都会有性能消耗。一次函数调用本身的开销是固定的,与更大的函数相比,调用小函数的代价更大,因为在每次调用过程中它们做的有用的工作更少。

因此,消除这些开销的方法必须是要消除函数调用本身,Go 的编译器就是这么做的,在某些条件下通过用函数的内容来替换函数调用来实现。这个过程被称为内联,因为它在函数调用处把函数体展开了。

改进的优化机会

Cliff Click 博士把内联描述为现代编译器做的优化措施,像常量传播(LCTT 译注:此处作者笔误,原文为 constant proportion,修正为 constant propagation)和死代码消除一样,都是编译器的基本优化方法。实际上,内联可以让编译器看得更深,使编译器可以观察调用的特定函数的上下文内容,可以看到能继续简化或彻底消除的逻辑。由于可以递归地执行内联,因此不仅可以在每个独立的函数上下文处进行这种优化决策,也可以在整个函数调用链中进行。

实践中的内联

下面这个例子可以演示内联的影响:

package main

import "testing"

//go:noinline
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

var Result int

func BenchmarkMax(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ {
        r = max(-1, i)
    }
    Result = r
}

运行这个基准,会得到如下结果: 3

% go test -bench=. 
BenchmarkMax-4   530687617         2.24 ns/op

在我的 2015 MacBook Air 上 max(-1, i) 的耗时约为 2.24 纳秒。现在去掉 //go:noinline 编译指令,再看下结果:

% go test -bench=. 
BenchmarkMax-4   1000000000         0.514 ns/op

从 2.24 纳秒降到了 0.51 纳秒,或者从 benchstat 的结果可以看出,有 78% 的提升。

% benchstat {old,new}.txt
name   old time/op  new time/op  delta
Max-4  2.21ns ± 1%  0.49ns ± 6%  -77.96%  (p=0.000 n=18+19)

这个提升是从哪儿来的呢?

首先,移除掉函数调用以及与之关联的前置处理 4 是主要因素。把 max 函数的函数体在调用处展开,减少了处理器执行的指令数量并且消除了一些分支。

现在由于编译器优化了 BenchmarkMax,因此它可以看到 max 函数的内容,进而可以做更多的提升。当 max 被内联后,BenchmarkMax 呈现给编译器的样子,看起来是这样的:

func BenchmarkMax(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ {
        if -1 > i {
            r = -1
        } else {
            r = i
        }
    }
    Result = r
}

再运行一次基准,我们看一下手动内联的版本和编译器内联的版本的表现:

% benchstat {old,new}.txt
name   old time/op  new time/op  delta
Max-4  2.21ns ± 1%  0.48ns ± 3%  -78.14%  (p=0.000 n=18+18)

现在编译器能看到在 BenchmarkMax 里内联 max 的结果,可以执行以前不能执行的优化措施。例如,编译器注意到 i 初始值为 0,仅做自增操作,因此所有与 i 的比较都可以假定 i 不是负值。这样条件表达式 -1 > i 永远不是 true 5

证明了 -1 > i 永远不为 true 后,编译器可以把代码简化为:

func BenchmarkMax(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ {
        if false {
            r = -1
        } else {
            r = i
        }
    }
    Result = r
}

并且因为分支里是个常量,编译器可以通过下面的方式移除不会走到的分支:

func BenchmarkMax(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ {
        r = i
    }
    Result = r
}

这样,通过内联和由内联解锁的优化过程,编译器把表达式 r = max(-1, i)) 简化为 r = i

内联的限制

本文中我论述的内联称作 叶子内联 leaf inlining :把函数调用栈中最底层的函数在调用它的函数处展开的行为。内联是个递归的过程,当把函数内联到调用它的函数 A 处后,编译器会把内联后的结果代码再内联到 A 的调用方,这样持续内联下去。例如,下面的代码:

func BenchmarkMaxMaxMax(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ {
        r = max(max(-1, i), max(0, i))
    }
    Result = r
}

与之前的例子中的代码运行速度一样快,因为编译器可以对上面的代码重复地进行内联,也把代码简化到 r = i 表达式。

下一篇文章中,我会论述当 Go 编译器想要内联函数调用栈中间的某个函数时选用的另一种内联策略。最后我会论述编译器为了内联代码准备好要达到的极限,这个极限 Go 现在的能力还达不到。

相关文章:

  1. 使 Go 变快的 5 件事
  2. 为什么 Goroutine 的栈空间会无限增长?
  3. Go 中怎么写基准测试
  4. Go 中隐藏的编译指令

  1. 在 Go 中,一个方法就是一个有预先定义的形参和接受者的函数。假设这个方法不是通过接口调用的,调用一个无消耗的函数所消耗的代价与引入一个方法是相同的。
  2. 在 Go 1.14 以前,栈检查的前置处理也被垃圾回收器用于 STW,通过把所有活跃的 goroutine 栈空间设为 0,来强制它们切换为下一次函数调用时的运行时状态。这个机制最近被替换为一种新机制,新机制下运行时可以不用等 goroutine 进行函数调用就可以暂停 goroutine。
  3. 我用 //go:noinline 编译指令来阻止编译器内联 max。这是因为我想把内联 max 的影响与其他影响隔离开,而不是用 -gcflags='-l -N' 选项在全局范围内禁止优化。关于 //go: 注释在这篇文章中详细论述。
  4. 你可以自己通过比较 go test -bench=. -gcflags=-S 有无 //go:noinline 注释时的不同结果来验证一下。
  5. 你可以用 -gcflags=-d=ssa/prove/debug=on 选项来自己验证一下。

via: https://dave.cheney.net/2020/04/25/inlining-optimisations-in-go

作者:Dave Cheney 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Pop!\_OS 20.04 是一款令人印象深刻的基于 Ubuntu 的 Linux 发行版。我在这篇评论中回顾了其主要的新功能,并分享了我对最新版本的体验。

现在,Ubuntu 20.04 LTS 及其官方变体版本已经发布了 - 是时候看看 System76 的 Pop!\_OS 20.04 了,这是基于 Ubuntu 的最好的发行版之一。

老实说,Pop!\_OS 是我最喜欢的 Linux 发行版,主要用于我做的所有事情。

现在,Pop!\_OS 20.04 终于来了。是时候来看看它提供了哪些功能,以及你是否应该升级?

Pop!\_OS 20.04 LTS 中有什么新东西?

从视觉上看,Pop!\_OS 20.04 LTS 与 Pop!\_OS 19.10 并没有太大的区别。然而,你可以发现几个新功能和改进。

但是,如果你之前使用的是 Pop!\_OS 18.04 LTS,则可以发现有很多东西可以尝试。

随着 GNOME 3.36 的到来,及其带来的一些新功能,Pop!\_OS 20.04 成为了一个令人激动的版本。

总的来说,以下是一些主要的亮点。

  • 自动窗口平铺
  • 新的应用程序切换器和启动器
  • 在 Pop!\_Shop 中增加了对 Flatpack 的支持。
  • GNOME 3.36
  • Linux 内核 5.4
  • 改进的混合图形支持

虽然听起来很有趣,但我们还是来了解一下详细的变化,以及到目前为止 Pop!\_OS 20.04 的体验如何。

Pop!\_OS 20.04 中的用户体验提升

毫无疑问,很多 Linux 发行版都提供了开箱即用的用户体验。同样的,Ubuntu 20.04 LTS 也有一流的改进和功能

而对于 System76 的 Pop!\_OS,他们总是试图更进一步。并且,大多数新功能旨在通过提供有用的功能来改善用户体验。

在这里,我将介绍一些改进,其中包括 GNOME 3.36 和 Pop!\_OS 特有的一些功能。

支持系统托盘图标

总算是有了!这可能不是什么大的改变 —— 但 Pop!\_OS 以前没有支持系统托盘图标(或小程序图标)。

随着 20.04 LTS 的发布,默认情况就有了系统托盘,不需要任何扩展。

依靠系统托盘图标的程序可能并不多 —— 但它仍然是重要的东西。

就我而言,我以前无法在 Pop!\_OS 19.10 上使用 ActivityWatch —— 但现在可以了。

自动窗口平铺

自动窗口平铺是我一直想尝试的东西 —— 但从来没花时间使用过 i3 这样的平铺窗口管理器来设置它,更别说是 Regolith 桌面了。

在 Pop!\_OS 20.04 中,你就不需要这样做了。自动窗口平铺功能已经内置,无需设置。

它还提供了“显示活动提示”的选项,也就是说,它将高亮显示活动窗口以避免混淆。而且,你还可以调整窗口之间的间隙。

你可以在他们的官方视频中看到它是如何工作的:

而且,我得说,这是 Pop!\_OS 20.04 上最大的新增功能之一,有可能帮助你更有效地进行多任务处理。

即使每次使用该功能都很方便,但为了最大程度地利用它,最好是使用一个大于 21 英寸的显示屏(至少)! 而且,因为这个原因 —— 我真的很想把我的显示器也升级一下!

新的扩展应用

Pop!\_OS 内置了一些独特的 GNOME 扩展。但是,你不需要用 GNOME Tweaks 来管理扩展。

新增加的 “Extensions” 应用可以让你在 Pop!\_OS 20.04 上配置和管理扩展程序。

改进的通知中心

在新的 GNOME 3.36 中,通知中心的外观经过了改进。这里,我启用了黑暗模式。

新的应用程序切换器 & 启动器

你仍然可以用 ALT+TABSuper+TAB 来浏览正在运行的应用程序。

但是,当你有很多事情要做的时候,这很耗时。所以,在 Pop!\_OS 20.04上,你可以使用 Super+ / 激活应用程序切换器和启动器。

一旦你习惯了这个快捷键,它将是非常方便的东西。

除此以外,你可能会发现 Pop!\_OS 20.04 上的图标/窗口在视觉上有许多其它细微的改进。

新的登录界面

嗯,这是 GNOME 3.36 带来的一个明显的变化。但是,它看起来确实很不错!

Pop!\_Shop 支持 Flatpak

通常,Pop!\_Shop 已经是一个非常有用的东西了,包括它自有的在内,它带有一个巨大的软件仓库。

现在,在 Pop!\_OS 20.04 中,你可以用 Pop!\_Shop 安装任何可用软件的 Debian 包或 Flatpak(通过 Flathub) —— 当然,前提是某个软件有 Flatpak 软件包。

如果你没有使用 Pop!\_OS 20.04,你可能要看看如何在 Linux 上使用 Flatpak

就我个人而言,我并不是 Flatpak 的粉丝,但有些应用如 GIMP 需要你安装 Flatpak 包才能获得最新版本。所以,在 Pop!\_Shop 上直接支持了 Flatpak 绝对是一件好事。

键盘快捷键更改

如果你习惯了 Pop!\_OS 19.10 或更早的版本上现有的键盘快捷键,这可能会让你很烦。

不管是哪种情况,有几个重要的键盘快捷键变化可能会改善你的体验,如下:

  • 锁定屏幕:Super + L 改为 Super + Escape
  • 移动工作区:Super + 上/下箭头键 改为 Super + CTRL + 上/下箭头键
  • 关闭窗口:Super + W 变更为 Super + Q
  • 切换最大化:Super +向上箭头 改为 Super + M

Linux 内核 5.4

与其他大多数最新的 Linux 发行版相似,Pop!\_OS 20.04 搭载了 Linux 内核 5.4

所以,很明显,你可以期望获得对 exFAT 支持、改进的 AMD 图形兼容性以及它附带所有其他功能。

性能提升

尽管 Pop!\_OS 并不称自己是轻量级的 Linux 发行版,但它仍然是一个资源节约型的发行版。而且,有了 GNOME 3.36 的支持,它的速度应该足够快了。

考虑到我已经将 Pop!\_OS 作为主要发行版使用已经一年多了,我从来没有遇到过性能问题。这就是你安装了 Pop!\_OS 20.04 之后的资源使用情况(取决于你的系统配置)。

给你一个作为参考,我的台式机配置包括 i5-7400 处理器、16GB 内存(2400MHz)、NVIDIA GTX 1050ti 显卡和 SSD。

我不是一个系统基准测试的忠实拥护者,因为除非你去尝试,否则它并不能让你知道特定的应用或游戏的性能。

你可以试试 Phoronix 测试套件来分析你的系统表现。但是,Pop!\_OS 20.04 LTS 应该是一个很爽快的体验!

软件包更新 & 其他改进

尽管每个基于Ubuntu的发行版都受益于Ubuntu 20.04 LTS的改进,但也有一些 Pop!\_OS 特有的错误修复和改进。

除此之外,一些主要的应用程序/包(如 Firefox 75.0)也已经更新到了最新版本。

到现在为止,应该没有任何严重的错误,至少对我来说没有。

你可以在 GitHub 上查看他们的开发进度,以了解他们在测试期间已经修复的问题和发布后即将修复的问题。

下载 & 支持 Pop!\_OS 20.04

在这个版本中,System76 终于增加了一个可选的订阅模式来支持 Pop!\_OS 的开发。

你可以免费下载 Pop!\_OS 20.04 —— 但如果你想支持他们,我建议你只需要 $1/月就可以订阅。

我对 Pop OS 20.04 的看法

我必须提到的是,我正在为最新的 20.04 版本提供全新的墙纸。但是,这没什么大不了的。

有了窗口平铺功能、支持 flatpak,以及众多其他改进,到目前为止,我对 Pop!\_OS 20.04 的体验是一流的。另外,很高兴看到他们在一些流行软件的开箱即用支持上突出了他们对创意专业人士的关注。

Ubuntu 20.04 的所有优点,再加上 System76 的一些额外的加料,让我印象深刻!

你试过 Pop!\_OS 20.04 吗?请在下面的评论中告诉我你的想法。


via: https://itsfoss.com/pop-os-20-04-review/

作者:Ankush Das 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出