Go在谷歌:以软件工程为目的的语言设计

From: http://www.oschina.net/translate/go-at-google-language-design-in-the-service-of-software-engineering

 

1. 摘要

(本文是根据Rob Pike于2012年10月25日在Tucson, Arizona举行的SPLASH 2012大会上所做的主题演讲进行修改后所撰写的。)

针对我们在Google公司内开发软件基础设施时遇到的一些问题,我们于2007年末构思出Go编程语言。当今的计算领域同创建如今所使用的编程语言(使用最多的有C++、Java和Python)时的环境几乎没什么关系了。由多核处理器、系统的网络化、大规模计算机集群和Web编程模型带来的编程问题都是以迂回的方式而不是迎头而上的方式解决的。此外,程序的规模也已发生了变化:现在的服务器程序由成百上千甚至成千上万的程序员共同编写,源代码也以数百万行计,而且实际上还需要每天都进行更新。更加雪上加霜的是,即使在大型编译集群之上进行一次build,所花的时间也已长达数十分钟甚至数小时。

之所以设计开发Go,就是为了提高这种环境下的工作效率。Go语言设计时考虑的因素,除了大家较为了解的内置并发和内存垃圾自动回收这些方面之外,还包括严格的依赖管理、对随系统增大而在体系结构方面发生变化的适应性、跨组件边界的健壮性(robustness)。

本文将详细讲解在构造一门轻量级并让人感觉愉悦的、高效的编译型编程语言时,这些问题是如何得到解决的。讲解过程中使用的例子都是来自Google公司中所遇到的现实问题。

 

2. 简介

Go语言开发自Google,是一门支持并发编程和内存垃圾回收的编译型静态类型语言。它是一个开源的项目:Google从公共的代码库中导入代码而不是相反。

Go语言运行效率高,具有较强的可伸缩性(scalable),而且使用它进行工作时的效率也很高。有些程序员发现用它编程很有意思;还有一些程序员认为它缺乏想象力甚至很烦人。在本文中我们将解释为什么这两种观点并不相互矛盾。Go是为解决Google在软件开发中遇到的问题而设计的,虽然因此而设计出的语言不会是一门在研究领域里具有突破性进展的语言,但它却是大型软件项目中软件工程方面的一个非常棒的工具。

 

3. Google公司中的Go语言

为了帮助解决Google自己的问题,Google设计了Go这门编程语言,可以说,Google有很大的问题。

硬件的规模很大而且软件的规模也很大。软件的代码行数以百万计,服务器软件绝大多数用的是C++,还有很多用的是Java,剩下的一部分还用到了Python。成千上万的工程师在这些代码上工作,这些代码位于由所有软件组成的一棵树上的“头部”,所以每天这棵树的各个层次都会发生大量的修改动作。尽管使用了一个大型自主设计的分布式Build系统才让这种规模的开发变得可行,但这个规模还是太大 了。

当然,所有这些软件都是运行在无数台机器之上的,但这些无数台的机器只是被看做数量并不多若干互相独立而仅通过网络互相连接的计算机集群。

简言之,Google公司的开发规模很大,速度可能会比较慢,看上去往往也比较笨拙。但很效果。

Go项目的目标是要消除Google公司软件开发中的慢速和笨拙,从而让开发过程更加高效并且更加具有可伸缩性。该语言的设计者和使用者都是要为大型软件系统编写、阅读和调试以及维护代码的人。

因此,Go语言的目的不是要在编程语言设计方面进行科研;它要能为它的设计者以及设计者的同事们改善工作环境。Go语言考虑更多的是软件工程而不是编程语言方面的科研。或者,换句话说,它是为软件工程服务而进行的语言设计。

但是,编程语言怎么会对软件工程有所帮助呢?下文就是该问题的答案。

4. 痛之所在

当Go刚推出来时,有人认为它缺乏某些大家公认的现代编程语言中所特有的特性或方法论。缺了这些东西,Go语言怎么可能会有存在的价值?我们回答这个问题的答案在于,Go的确具有一些特性,而这些特性可以解决困扰大规模软件开发的一些问题。这些问题包括:

  • Build速度缓慢
  • 失控的依赖关系
  • 每个程序员使用同一门语言的不同子集
  • 程序难以理解(代码难以阅读,文档不全面等待)
  • 很多重复性的劳动
  • 更新的代价大
  • 版本偏斜(version skew)
  • 难以编写自动化工具
  • 语言交叉Build(cross-language build)产生的问题

一门语言每个单个的特性都解决不了这些问题。这需要从软件工程的大局观,而在Go语言的设计中我们试图致力于解决所有这些问题。

举个简单而独立的例子,我们来看看程序结果的表示方式。有些评论者反对Go中使用象C一样用花括号表示块结构,他们更喜欢Python或Haskell风格式,使用空格表示缩进。可是,我们无数次地碰到过以下这种由语言交叉Build造成的Build和测试失败:通过类似SWIG调用的方式,将一段Python代码嵌入到另外一种语言中,由于修改了这段代码周围的一些代码的缩进格式,从而导致Python代码也出乎意料地出问题了并且还非常难以觉察。 因此,我们的观点是,虽然空格缩进对于小规模的程序来说非常适用,但对大点的程序可不尽然,而且程序规模越大、代码库中的代码语言种类越多,空格缩进造成的问题就会越多。为了安全可靠,舍弃这点便利还是更好一点,因此Go采用了花括号表示的语句块。

5.C和C++中的依赖

在处理包依赖(package dependency)时会出现一些伸缩性以及其它方面的问题,这些问题可以更加实质性的说明上个小结中提出的问题。让我们先来回顾一下C和C++是如何处理包依赖的。

ANSI C第一次进行标准化是在1989年,它提倡要在标准的头文件中使用#ifndef这样的"防护措施"。 这个观点现已广泛采用,就是要求每个头文件都要用一个条件编译语句(clause)括起来,这样就可以将该头文件包含多次而不会导致编译错误。比如,Unix中的头文件<sys/stat.h>看上去大致是这样的:

?
1
2
3
4
5
/* Large copyright and licensing notice */
#ifndef _SYS_STAT_H_
#define _SYS_STAT_H_
/* Types and other definitions */
#endif

此举的目的是让C的预处理器在第二次以及以后读到该文件时要完全忽略该头文件。符号_SYS_STAT_H_在文件第一次读到时进行定义,可以“防止”后继的调用。

这么设计有一些好处,最重要的是可以让每个头文件能够安全地include它所有的依赖,即时其它的头文件也有同样的include语句也不会出问题。 如果遵循此规则,就可以通过对所有的#include语句按字母顺序进行排序,让代码看上去更整洁。

但是,这种设计的可伸缩性非常差。

在1984年,有人发现在编译Unix中ps命令的源程序ps.c时,在整个的预处理过程中,它包含了<sys/stat.h>这个头文件37次之多。尽管在这么多次的包含中有36次它的文件的内容都不会被包含进来,但绝大多数C编译器实现都会把"打开文件并读取文件内容然后进行字符串扫描"这串动作做37遍。这么做可真不聪明,实际上,C语言的预处理器要处理的宏具有如此复杂的语义,其势必导致这种行为。

对软件产生的效果就是在C程序中不断的堆积#include语句。多加一些#include语句并不会导致程序出问题,而且想判断出其中哪些是再也不需要了的也很困难。删除一条#include语句然后再进行编译也不太足以判断出来,因为还可能有另外一条#include所包含的文件中本身还包含了你刚刚删除的那条#include语句。

从技术角度讲,事情并不一定非得弄成这样。在意识到使用#ifndef这种防护措施所带来的长期问题之后,Plan 9的library的设计者采取了一种不同的、非ANSI标准的方法。Plan 9禁止在头文件中使用#include语句,并要求将所有的#include语句放到顶层的C文件中。 当然,这么做需要一些训练 —— 程序员需要一次列出所有需要的依赖,还要以正确的顺序排列 —— 但是文档可以帮忙而且实践中效果也非常好。这么做的结果是,一个C源程序文件无论需要多少依赖,在对它进行编译时,每个#include文件只会被读一次。当然,这样一来,对于任何#include语句都可以通过先拿掉然后在进行编译的方式判断出这条#include语句到底有无include的必要:当且仅当不需要该依赖时,拿掉#include后的源程序才能仍然可以通过编译。

Plan 9的这种方式产生的一个最重要的结果是编译速度比以前快了很多:采用这种方式后编译过程中所需的I/O量,同采用#ifndef的库相比,显著地减少了不少。

但在Plan 9之外,那种“防护”式的方式依然是C和C++编程实践中大家广为接受的方式。实际上,C++还恶化了该问题,因为它把这种防护措施使用到了更细的粒度之上。按照惯例,C++程序通常采用每个类或者一小组相关的类拥有一个头文件这种结构,这种分组方式要更小,比方说,同<stdio.h>相比要小。因而其依赖树更加错综复杂,它反映的不是对库的依赖而是对完整类型层次结构的依赖。而且,C++的头文件通常包含真正的代码 —— 类型、方法以及模板声明 ——不像一般的C语言头文件里面仅仅有一些简单的常量定义和函数签名。这样,C++就把更多的工作推给了编译器,这些东西编译起来要更难一些,而且每次编译时编译器都必须重复处理这些信息。当要build一个比较大型的C++二进制程序时,编译器可能需要成千上万次地处理头文件<string>以了解字符串的表示方式。(根据当时的记录,大约在1984年,Tom Cargill说道,在C++中使用C预处理器来处理依赖管理将是个长期的不利因素,这个问题应该得到解决。)

在Google,Build一个单个的C++二进制文件就能够数万次地打开并读取数百个头文件中的每个头文件。在2007年,Google的build工程师们编译了一次Google里一个比较主要的C++二进制程序。该文件包含了两千个文件,如果只是将这些文件串接到一起,总大型为4.2M。将#include完全扩展完成后,就有8G的内容丢给编译器编译,也就是说,C++源代码中的每个自己都膨胀成到了2000字节。 还有一个数据是,在2003年Google的Build系统转变了做法,在每个目录中安排了一个Makefile,这样可以让依赖更加清晰明了并且也能好的进行管理。一般的二进制文件大小都减小了40%,就因为记录了更准确的依赖关系。即使如此,C++(或者说C引起的这个问题)的特性使得自动对依赖关系进行验证无法得以实现,直到今天我们仍然我发准确掌握Google中大型的C++二进制程序的依赖要求的具体情况。
由于这种失控的依赖关系以及程序的规模非常之大,所以在单个的计算机上build出Google的服务器二进制程序就变得不太实际了,因此我们创建了一个大型分布式编译系统。该系统非常复杂(这个Build系统本身也是个大型程序)还使用了大量机器以及大量缓存,藉此在Google进行Build才算行得通了,尽管还是有些困难。 即时采用了分布式Build系统,在Google进行一次大规模的build仍需要花几十分钟的时间才能完成。前文提到的2007年那个二进制程序使用上一版本的分布式build系统花了45分钟进行build。现在所花的时间是27分钟,但是,这个程序的长度以及它的依赖关系在此期间当然也增加了。为了按比例增大build系统而在工程方面所付出的劳动刚刚比软件创建的增长速度提前了一小步。

6. 走进 Go 语言

当编译缓慢进行时,我们有充足的时间来思考。关于 Go 的起源有一个传说,话说正是一次长达45分钟的编译过程中,Go 的设想出现了。人们深信,为类似谷歌网络服务这样的大型程序编写一门新的语言是很有意义的,软件工程师们认为这将极大的改善谷歌程序员的生活质量。

尽管现在的讨论更专注于依赖关系,这里依然还有很多其他需要关注的问题。这一门成功语言的主要因素是:

  • 它必须适应于大规模开发,如拥有大量依赖的大型程序,且又一个很大的程序员团队为之工作。
  • 它必须是熟悉的,大致为 C 风格的。谷歌的程序员在职业生涯的早期,对函数式语言,特别是 C 家族更加熟稔。要想程序员用一门新语言快速开发,新语言的语法不能过于激进。
  • 它必须是现代的。C、C++以及Java的某些方面,已经过于老旧,设计于多核计算机、网络和网络应用出现之前。新方法能够满足现代世界的特性,例如内置的并发。

说完了背景,现在让我们从软件工程的角度谈一谈 Go 语言的设计。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序,用户拒绝授权后重新授权;uni-app小程序,用户拒绝授权后点击无效;重新进入后拉起位置授权框;

问题&#xff1a;当用户第一次进入小程序&#xff0c;点击授权按钮后&#xff0c;点了拒绝&#xff0c;再次点击不会出现授权页面&#xff0c;只有再次进入小程序的时候&#xff0c;才会出发请求授权 。 案例&#xff1a; 假如我们获取微信位置&#xff0c;第一次点击的时候弹起…

运维工程师必会的109个Linux命令

运维工程师必会的109个Linux命令 版本1.0 崔存新 更新于2009-12-26 目录 1 文件管理 6 1.1 basename 6 1.2 cat 6 1.3 cd 7 1.4 chgrp 7 1.5 chmod 8 1.6 chown 9 1.7 comm 10 1.8 cp 10 1.9 cut 11 1.10 dd 12 1.11 diff 13 1.12 dir 14 1.13 dos2unix 16 1.14 egrep 17 1.15…

网卡混杂模式介绍与设置

1.混杂模式介绍 混杂模式就是接收所有经过网卡的数据包&#xff0c;包括不是发给本机的包。默认情况下网卡只把发给本机的包&#xff08;包括广播包&#xff09;传递给上层程序&#xff0c;其它的包一律丢弃。简单的讲,混杂模式就是指网卡能接受所有通过它的数据流&#xff0c…

eclipse使用技巧---使用正则表达式查找替换

1&#xff0c;Eclipse ctrlf 打开查找框2&#xff0c;选中 Regular expressions (正则表达式)去掉/* */(eclipse) /\*(.|[\r\n])*?\*/去掉//(eclipse) //.*$去掉import(eclipse) import.*$去掉空行(eclipse) ^\s*\n去掉空行(ue) …

​浅拷贝与深拷贝​

浅拷贝 与深拷贝 一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol)和对象数据类型。 基本数据类型的特点&#xff1a;直接存储在栈(stack)中的数据 引用数据类型的特点&#xff1a;存储的是该对象在栈中引用&#xff0c;真实…

更改微信小程序的基础版本库;更改uni-app小程序基础库;更改用户的微信小程序基础库最低版本;设置用户的微信小程序版本库;

需求场景&#xff1a;微信小程序不少API都有最低版本支持&#xff0c;为了避免不必要的麻烦&#xff0c;我们可以根据需要给小程序设置基础库最低版本&#xff0c;这样若用户使用的基础库版本低于设置的最低版本要求&#xff0c;则无法正常使用小程序&#xff0c;并提示更新微信…

[python-thirdLib] Python中第三方的用于解析HTML的库:BeautifulSoup

From: http://www.crifan.com/python_third_party_lib_html_parser_beautifulsoup/ 背景 在Python去写爬虫&#xff0c;网页解析等过程中&#xff0c;比如&#xff1a; 如何用Python&#xff0c;C#等语言去实现抓取静态网页抓取动态网页模拟登陆网站 常常需要涉及到HTML等网…

VMware 常见使用问题梳理

1.“Transport(VMDB)error -44:Message” 这种情况说明虚拟机的一个服务没有开启&#xff0c;在本机中找到服务&#xff1a;“打开运行”-“services.msc”回车。找到VMware Authorization Service这个服务&#xff0c;启动起来就OK了。 2.提示”此虚拟机被配置为64位操作系统…

图灵社区 : 阅读 : 谁说Vim不是IDE?(三)

图灵社区 : 阅读 : 谁说Vim不是IDE&#xff1f;&#xff08;三&#xff09;Powerline1、下载地址https://github.com/Lokaltog/vim-powerline2、功能说明Powerline是Vim的一个非常漂亮的状态栏插件&#xff0c;安装了Powerline之后&#xff0c;Vim底部将会出现一个增强型状态栏…

uni-app小程序onShow执行两次;微信小程序onShow重复执行原因;导航栏tabBar页的onLoad函数不执行;App.vue页的onShow执行原因;onShow莫名其妙执行

1.只有五种情况会触发导航栏tabBar页的onLoad函数&#xff0c;分别是&#xff1a; –1.1&#xff1a;首次进入到导航栏tabBar页面&#xff1b; –1.2&#xff1a;从微信分享进入的导航栏tabBar页面&#xff1b; –1.3&#xff1a;识别二维码跳转到小程序的导航栏tabBar页面&…

用fiddler抓包小程序

第一步&#xff1a;安装fiddler,保证手机和PC端在同一个wifi下&#xff1b; 第二步&#xff1a;设置属性按图勾选第三步&#xff1a;以上两步设置完后&#xff0c;重启下fiddler(解决本地服务器不能访问)&#xff0c;然后查看本地IP地址第四步&#xff1a;手机设置HTTP代理 我的…

[Python2.x] 标准库 urllib2 的使用细节

From: http://www.cnblogs.com/yuxc/archive/2011/08/01/2123995.html 刚好用到&#xff0c;这篇文章写得不错&#xff0c;转过来收藏。 转载自 道可道 | Python 标准库 urllib2 的使用细节 Python 标准库中有很多实用的工具类&#xff0c;但是在具体使用时&#xff0c;标准…

草稿--Windows消息机制

消息本身是作为一个记录传递给应用程序的&#xff0c;这个记录中包含了消息的类型以及其他信息。 MSG含有来自windows应用程序消息队列的消息信息&#xff0c;它在Windows中声明如下&#xff1a;typedef struct tagMsg{HWND hwnd; // 接受该消息的窗口句柄UINT mes…

微信小程序保存图片到相册;uni-app小程序保存网络图片到相册;小程序保存图片到相册拒绝授权后重新拉起授权;保存图片到系统相册;小程序保存图片测试可以,真机保存图片失败

文末代码可以直接复制使用&#xff0c;图片修改成你的图片路径即可 一、场景&#xff1a; 小程序点击按钮&#xff0c;保存项目内的静态图片或者微信头像或者后端返回的图片&#xff1b; 二、注意点及思路拆分&#xff1a; –2.1&#xff1a;小程序保存图片功能&#xff0c;必须…

数组(待完成)

创建 字面量方式 var arr []; var arr ["成员1", 2];//可以是不同成员构造函数方式 空的 var array new Array(); 指定长度 var array2 new Array(10); 成员值都是undefined。此方式有技巧使用 虽然值是undefined&#xff0c;但join后不会出现undefined字符串 比…

VMware打开虚拟机黑屏

排除系统崩溃的情况&#xff0c;比如开机输入密码之后系统黑屏&#xff0c;此原因是由于显示选项开启了3D加速导致的&#xff0c;具体修改步骤&#xff1a; VM->Settings->Hardware->Display 在右面的内容栏中将 Accelerate 3D graphics 取消打勾&#xff0c;然后重启…

[python3.x] 利用chardet检测网页编码

环境&#xff1a;Win7_x64 python3.4.3 需要先下载chardet并进行安装&#xff0c;下载地址&#xff1a;https://pypi.python.org/packages/source/c/chardet/chardet-2.3.0.tar.gz 安装&#xff1a;进入解压后的目录&#xff0c;在命令窗口执行: python setup.py install 写…

js moment时间戳与时间格式相互转换

var moment require(moment) 获取时间戳 : var res moment(Date.now(), YYYY-MM-DD HH:mm:ss).valueOf(); 获取格式时间: var res moment(Date.now()).format(YYYY-MM-DD HH:mm:ss);

微信小程序中base64转换成图片;uni-app小程序base64转图片;微信小程序base64文件转图片;微信小程序base64图片转图片

将微信小程序的图片转成base64 点击此链接看另一篇 以下是将后端返回的base64转成图片&#xff1a; 方法1&#xff1a;使用微信小程序自带方法 //把base64转换成图片getBase64ImageUrl: (base64Url) {/// 获取到base64Datavar base64Data base64Url;/// 通过微信小程序自带方…

PHP流式上传和表单上传(美图秀秀)

最近需要开发一个头像上传的功能&#xff0c;找了很多都需要授权的&#xff0c;后来找到了美图秀秀&#xff0c;功能非常好用。 <?php /*** Note:for octet-stream upload* 这个是流式上传PHP文件* Please be amended accordingly based on the actual situation*/ $post_i…