Yumiku

Yumiku 查看完整档案

杭州编辑浙江大学  |  计算机科学与技术 编辑IDEA Lab  |  程序员 编辑 rheabubbles.github.io/ 编辑
编辑

只想做一个简简单单的程序员,偶尔学点算法,尝试尽力但不一定能定期更新,主要是一些后端深入、算法基础讲解(经典算法和人工智能算法)。

目标是在2021年2月找到提前批实习
-`д´-

个人动态

Yumiku 发布了文章 · 2020-04-13

2021算法岗基础技能树怎么点?

title.jpg

文章创作于2020年4月,大约7000字,预计阅读时间18分钟,请坐和放宽。

0 - 前言

注:本文默认传统算法是所有工程师的基础技能,所以后面提到的算法主要指机器学习以及深度学习等算法。

尽管目前本人求职的重心还是在后端上,但是为了能从现在的人工智能专业硕士顺利毕业,也为了让自己顺便拓展算法工程这条路,简单的规划一下算法这部分需要补的知识和技能还是有必要的。

本文以拿到2021算法岗Offer为目标,从2020的算法岗面经入手,分析需要点的技能树都有哪些。

1 - 不同算法岗的区别

首先需要说明的一个地方就是,不同领域的技术岗位,都会按照接触科研和业务的程度来进行一定的区分。

可以简单参考知乎上霍华德在问题“学术界科研(research)与工业界研发(R&D)有什么区别?”的回答[1],算法岗可以大致分为:

  • 业务导向,大部分情况下以Development为主;
  • 技术导向,Research和Development兼顾;
  • 科研导向,大部分情况下会Research为主;

近几年这一点在算法岗上表现体现的比较明显,因为在几年前大部分算法都还只在研究阶段,而最近随着一些成熟的机器学习封装库被开放过后,部分行业里开始发现这些算法可以产生实际的价值,所以算法岗位也就呈现了近几年的爆发式增长,尤其是业务导向的算法工程师(因为大部分公司还是希望这些算法能更多更快的产出业务价值)。

当然这话其实说的已经有点晚了,现在已经不是刚开始那样的时候了,那个时候会使用框架、调调参就可以拿到Offer,现在的算法岗更像是浪潮过后的归于正常的情况,不仅需要扎实的理论基础,还需要丰富的项目实践。

我个人是更倾向于业务导向的算法工程,所以本文以这部分为目标来编写,如果你有兴趣了解三种不同岗位的细节,可以阅读夕小瑶的一篇公众号文章《拒绝跟风,谈谈几种算法岗的区别和体验》[2]。

2 - 2020面经读后感

为了更好地了解各行业公司都比较看重哪些方面的东西(很可能也都是这些公司在用的技术),我选择直接从算法岗的面经里去寻找可能的答案,面经贴主要是牛客网上的[3]。

找到的点可以简单分为以下几类:

  • 纯数学相关
  • 机器学习
  • 深度学习
  • NLP相关
  • 推荐算法

一些传统算法相关就不在此列了(Leetcode和一些书比如《剑指Offer》整理的也足够多了)。我能够看到的面经是有限的,面经里提供的内容也是有限的,所以后面的内容不能说能概括到全部,但是至少能提取出很大一部分频繁出现的关键词(如果真的有需要的话再写个爬虫+关键词提取吧)。

内容因为并没有特别多的先后依赖关系,所以就按照在面经里出现的顺序来列了。

2.1 - 纯数学相关

  • 事件概率计算
  • 狄利克雷分布
  • 最大似然估计和贝叶斯估计
  • ...

2.2 - 机器学习

  • 数据清洗、数据平滑
  • 常用的降维方式、PCA
  • LDA(Linear Discriminant Analysis)
  • 决策树,ID3、C4.5、CART
  • XGBoost、LightGBM、随机森林、Adaboost、GBDT
  • SVM原理、对偶问题
  • L1、L2正则化
  • 过拟合
  • 特征选择方法
  • LR(Logistic Regression)和SVM、Linear SVM 和 LR
  • 聚类方法、K-means、层次聚类
  • 模型的评价指标、ROC
  • 朴素贝叶斯原理
  • scikit-learn、numpy
  • bagging和boosting
  • 集成学习
  • 分类方法
  • 模型上线优化
  • 连续值、离散值,离散化连续特征的好处
  • 回归方法、线性回归、岭回归、Lasso回归、LR
  • 信息增益,信息增益比,Gini系数的关系
  • One-Hot编码的原理及意义
  • Optimizers(Gradient Descent、...)
  • 统计学习算法
  • ...

2.3 - 深度学习

  • Feedforward Neural Network
  • Back Propagation
  • Layers,convolutional、pooling、full connected
  • CNN(卷积)、RNN(梯度消失问题)、LSTM、GRU
  • GAN
  • 目标检测,R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD、...
  • SoftMax、Sigmoid
  • Embedding
  • 注意力机制
  • GCN(Graph Convolutional Network)
  • Optimizers(Gradient Descent、BGD、SGD、Adam、Adagard...)
  • Tensorflow、Keras、PyTorch
  • Activation(sigmoid、softmax、relu...)
  • MobileNet
  • Dropout
  • CPU、GPU加速
  • ...

2.4 - NLP相关

  • 关键字提取、TF-IDF
  • 命名实体
  • LDA(Latent Dirichlet Allocation)
  • word2vec
  • Bert、Transformer
  • ...

2.5 - 推荐算法

  • 基于内容的推荐
  • 协同过滤推荐、UserCF、ItemCF
  • 如何处理稀疏矩阵
  • ...

2.6 - 面经总结

在大部分算法面试中,面试官的问题都是围绕着简历上的项目来问的,我们可以看到上面的很多项目所涉及到的点,面试官都有可能往深了问,比如:

  • SVM原始问题为什么要转化为对偶问题,为什么对偶问题就好求解,原始问题不能求解么?
  • K-means 中我想聚成100类 结果发现只能聚成98类,为什么?
  • LR和SVM这两个应用起来有什么不同?
  • 对于PCA,会有第一主成分、第二主成分,怎么为什么第一主成分是第一,原因是什么?
  • bagging 和boosting 哪个可以让结果的方差更小一些,为什么?
  • ...

所以在学习过程中不光要知道How,还是要多知道几个Why,一是为了能在面试的时候能回答出问题,二是为了更好地理解手里的这个工具。

3 - 算法的基础技能树

面经总结出来的点也还是有点乱,所以我又参考了一些算法学习路线的帖子来简单的归类梳理一下各个点,主要是参考的机器之心的这篇完备的 AI 学习路线,最详细的中英文资源整理[4],时效为2019-04-28,还参考了一个不知道我什么时候在哪里找到的知识点总结图,如果有人知道出处的话可以在评论里和我说一下,图片链接会附在文章末尾(图片很大,所以放进来会看不清)。

3.1 - 数学基础

  • 高等数学
  • 线性代数
  • 概率论与数理统计

并不是说要把上面三个教材吃的完全透了才开始学习后面的,其实人工智能领域很多方法都只是用到了其中的一小部分,有一些专门总结了的机器学习中需要使用到的数学知识的书籍和文档[4],你可以在机器之心的公众号文章找到这些(我在文章末尾也会上一个链接),具有基本的数学基础的可以用来复习,没有数学基础的还是建议在看不懂的地方回顾到教材。

有些帖子可能会在数学基础这部分加上一个凸优化,个人理解上,在纯粹的学习过程中凸优化可以说是最枯燥的一门课,里面大部分是一些凸优化的定义和理论公式的证明,所以建议在后期遇到的时候再切入某一个点深入学习。

3.2 - 编程基础

在数值分析和人工智能这方面,还是Python支持的库比较方便,在入门学习方面已经足够使用了,版本目前推荐3.5 or 3.6。

Anaconda(or Miniconda)是一个比较方便的Python虚拟环境和包管理软件,但是在某些时候会遇到麻烦事(比如一些算法框架的奇奇怪怪的环境要求),但是在大部分情况下的入门阶段已经足够使用了。

Python的IDE大部分人常用的就是Pycharm,如果有些能力折腾的,可以考虑用vscode+插件等等。

3.3 - 数据处理/分析/挖掘

实际使用中,很多机器学习、深度学习方法只有在高质量数据中才能起作用,比如数据的信息量足够多、噪声和错误信息足够少。而实际数据收集过程中,很多情况下不可能让数据这么完美,所以需要进行一些初步的数据处理(采集、清洗、采样、去噪、降维、...)。

除了Python语言基础,还需要掌握一些基础的数据处理库,比如numpy、pandas、matplotlib等,可以参考机器之心推荐的《利用python进行数据分析》。

这本书含有大量的实践案例,你将学会如何利用各种Python库(包括NumPy,Pandas、Matplotlib以及IPython等)高效地解决各式各样的数据分析问题。如果把代码都运行一次,基本上就能解决数据分析的大部分问题了。

另外还有就是[4]:

数据挖掘可以帮助我们初步的理解数据各特征之间具有的一些关系,增加或者删除一些特征来帮助后续的学习。数据挖掘可以通过一些导论书籍或者课程进行一些初步系统性的了解,其中的大部分原理都不是很高深。

3.4 - 传统机器学习

3.4.1 - 入门

如果在入门的时候,一开始就学习数学和理论公式,也不去弄明白这个东西到底有什么用,就很难去理解到底为什么需要这些理论。

在学习每个机器学习算法前,可以先笼统的明白这个东西的作用,然后带着问题“这个是怎么实现的?”去探究算法的理论,才能比较贯通的理解其中的数学和公式。

这里推荐一个网站,产品经理的人工智能学习库

人工智能领域的百科全书,非常适合小白和新手入门 AI 领域。现在市面上大家看到的绝大部分 AI 资料都是追求严谨的“理工科天书”,这个世界不缺少严谨真确晦涩难懂的 AI 资料,但是很缺容易理解的内容。我们希望抛开复杂的公式,复杂的逻辑,复杂的专用名词。做一套文科生也能看懂的 AI 知识库。

3.4.2 - 理论

机器学习的理论部分大概有:

  • 机器学习所面向的问题

    • 分类

      • 决策树
      • K-近邻
      • SVM
      • Logistic回归
      • 贝叶斯
      • 随机森林
      • ...
    • 回归

      • 线性回归
      • 最小二乘回归
      • 局部回归
      • 神经网络
      • ...
    • 聚类

      • K-means
      • EM
      • ...
    • 降维

      • 主成分分析 PCA
      • 线性判别分析 LDA
      • ...
    • ...
  • 回归

    • 线性回归
    • Logistic回归
    • ...
  • 决策树与随机森林

    • ID3
    • C4.5
    • CART
    • 回归树
    • 随机森林
    • ...
  • SVM

    • 线性可分
    • 线性不可分
  • 最大熵与EM算法
  • 多算法组合与模型优化

    • 模型选择
    • 模型状态分析
    • 模型优化
    • 模型融合
  • 贝叶斯网络
  • 隐马尔可夫链HMM

    • 马尔可夫链
    • 隐马尔可夫链
  • 主题模型LDA
  • 集成学习
  • ...

内心OS:这总结下来基本上和某些书的目录差不多了。

推荐课程[4]:

推荐书籍[4]:

  • 西瓜书《机器学习》- 周志华,主要是机器学习的核心数学理论和算法。
  • 《统计学习方法》- 李航,更加完备和专业的机器学习理论知识,作为夯实理论非常不错。
  • 《Pattern Recognition and Machine Learning》,中文译名《模式识别与机器学习》,简称PRML,出自微软剑桥研究院实验室主任 克里斯托弗·毕晓普(Christopher Bishop)之手,豆瓣评分9.5,目前这本书已经被微软开源,地址:https://www.microsoft.com/en-...,书是英文的,网上可以找到一些第三方的中文翻译,不过还是建议读英文,再次也是中英对照着来。

3.4.3 - 实践

在初步入门和学习理论后,为了活学活用学到的算法,可以尝试进行实践。

首先是一些可以拓展能力的常用工具(免得自己造轮子):

  • scikit-learn:一个Python第三方提供的非常强力的机器学习库,它包含了从数据预处理到训练模型的各个方面。在实战使用scikit-learn中可以极大的节省我们编写代码的时间以及减少我们的代码量,使我们有更多的精力去分析数据分布,调整模型和修改超参。
  • XGBoost:xgboost是大规模并行boosted tree的工具,它是目前最快最好的开源boosted tree工具包,比常见的工具包快10倍以上。在数据科学方面,有大量kaggle选手选用它进行数据挖掘比赛,其中包括两个以上kaggle比赛的夺冠方案。在工业界规模方面,xgboost的分布式版本有广泛的可移植性,支持在YARN, MPI, Sungrid Engine等各个平台上面运行,并且保留了单机并行版本的各种优化,使得它可以很好地解决于工业界规模的问题。
  • LightBGM:​ LightGBM(Light Gradient Boosting Machine)同样是一款基于决策树算法的分布式梯度提升框架。为了满足工业界缩短模型计算时间的需求,LightGBM的设计思路主要是两点:1. 减小数据对内存的使用,保证单个机器在不牺牲速度的情况下,尽可能地用上更多的数据;2. 减小通信的代价,提升多机并行时的效率,实现在计算上的线性加速。由此可见,LightGBM的设计初衷就是提供一个快速高效、低内存占用、高准确度、支持并行和大规模数据处理的数据科学工具。
  • ...

然后就可以去Kaggle上和大佬们对线了,如果你有能力也有idea,可以自己开出一个项目来做。

如果你对某些算法有更深程度的理解,你甚至可以尝试用自己代码复现这些算法。

推荐书籍:

  • 《Scikit-Learn 与 TensorFlow 机器学习使用指南》:这本书分为两大部分,第一部分介绍机器学习基础算法,每章都配备 Scikit-Learn 实操项目;第二部分介绍神经网络与深度学习,每章配备 TensorFlow 实操项目。如果只是机器学习,可先看第一部分的内容。

3.5 - 深度学习

3.5.1 - 入门

在这里同样推荐产品经理的人工智能学习库

3.5.2 - 理论

深度学习的理论部分大概有[4]:

  • 基础神经网络

    • 神经元
    • 激活函数
    • 基本结构:输入层、隐藏层、输出层
    • 反向传播算法
  • CNN

    • 卷积层
    • 池化层
    • 全连接层
    • CNN的典型网络结构(LeNet, AlexNet, VGG, ResNet, ...)
  • RNN

    • 单向RNN
    • 双向RNN
    • 深度RNN
    • LSTM
    • GRU
  • GAN
  • ...

你可以从广度上入手,在都了解的基础上,选择一个方向进行深入学习:

  • 计算机视觉(图像、视频处理,主要用CNN);
  • 自然语言处理NLP(包括文本、语音处理,序列数据往往需要RNN);
  • 生成模型(GAN、VAE等等);

推荐课程[4]:

推荐书籍[4]:

  • 开源书籍《神经网络与深度学习》 - 复旦邱锡鹏,这本书花费了邱老师三年的时间,将自己的研究,日常的教学和实践结合梳理出这个深度学习知识体系。该书主要介绍神经网络与深度学习中的基础知识、主要模型(前馈网络、卷积网络、循环网络等)以及在计算机视觉、自然语言处理等领域的应用[5]。
  • 花书《深度学习》,源:Github网友翻译,该书从浅入深介绍了基础数学知识、机器学习经验以及现阶段深度学习的理论和发展,它能帮助人工智能技术爱好者和从业人员在三位专家学者的思维带领下全方位了解深度学习。
  • 神贴《深度学习 500 问》,作者是川大的一名优秀毕业生谈继勇。该项目以深度学习面试问答形式,收集了 500 个问题和答案。内容涉及了常用的概率知识、线性代数、机器学习、深度学习、计算机视觉等热点问题,该书目前尚未完结,却已经收获了Github 2.4w stars(现在已经3.7w star了)。

3.5.3 - 实践

在初步入门和学习理论后,为了活学活用学到的深度学习算法,可以尝试进行实践。

首先是一些可以拓展能力的常用工具(免得自己造轮子):

  • TensorFlow,Google开源的深度学习框架,不过接口都比较底层,可能入门级稍难。
  • Keras,一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。Keras对入门友好,不过其中过多的封装可能会导致需要自定义修改比较麻烦,所以他们主要面向的是快速实验、快速验证的任务。
  • PyTorch,Facebook发布的一套深度学习框架,PyTorch专注于直接处理数组表达式的较低级别 API。去年它受到了大量关注,成为学术研究和需要优化自定义表达式的深度学习应用偏好的解决方案。

关于哪个工具更好的问题,"支持者"之间也是争议不断,其实也不用纠结到底应该选哪一个,都试试不就知道了(逃。

选择一个工具学会后,就可以去Kaggle上和大佬们对线了,如果你有能力也有idea,可以自己开出一个项目来做。

3.6 - 其他

至于强化学习、迁移学习、计算机视觉、NLP、推荐系统、知识图谱等内容,限于文章篇幅,就不在这里介绍了,不过你可以在机器之心的那篇文章中找到和他们有关的内容。

3.7 - 论文阅读

机器学习、深度学习大部分理论内容都来自计算机科研领域发表的论文,当下的前沿技术也都在近几年发表的论文中。

作为入门、理论、实践的之后一个拓展阶段,可以通过阅读前沿论文来增加知识面。

由于前沿论文阅读并不能算是一个业务导向的算法工程师所必须具有的能力,所以在这就不做过多的介绍了,同样,你可以在机器之心的那篇文章中找到关于阅读前沿Paper的相关介绍。

4 - 总结

不久前,某404网站给我推送了一个视频,名字看起来非常标题党,Don't learn machine learning - Daniel Bourke,源:Youtube,其中作者核心的内容是不要为了只是学习算法而学习算法,要为了创造产品(或者说应用、或者说解决问题)而学习算法,有条件的同学可以看看(暂时还没有看到国内的翻译搬运,如果有时间有机会的话我就翻译搬运一下吧)。

面向Offer学习未必是最优的一条路。我的目标是以后端为主线发展,之所以还没有完全的放弃这部分的算法,一部分是因为我的专业,更多的原因是我知道在某些问题上只有这些算法才能有效地解决,会用更多的算法也可以让程序员解决更多的问题。

5 - 参考文章

查看原文

赞 1 收藏 1 评论 0

Yumiku 赞了文章 · 2020-04-10

libcsp: 一个 10 倍于 Golang 的高性能 C 语言并发库

libcsp是一个C语言实现的基于CSP模型的高性能并发库, 利用它你可以用C开发一些高性能项目.

特性:

  • 支持多核
  • 高性能调度器
  • 编译时栈大小静态分析
  • 高性能 Lock-free 通道
  • 支持 netpoll 和 timer

Golang和Libcsp比较

// Golang                                       // Libcsp
go foo(arg1, arg2, arg3)                        async(foo(arg1, arg2, arg3));
                                              
var wg sync.WaitGroup                           sync(foo(); bar());
wg.Add(2)                                            
go func() { defer wg.Done(); foo(); }()
go func() { defer wg.Done(); bar(); }()
wg.Wait()

runtime.Gosched()                               yield();
                                              
chn := make(chan int, 1 << 6)                   chan_t(int) *chn = chan_new(int)(6);
num = <-chn                                     chan_pop(chn, &num);
chn <- num                                      chan_push(chn, num);
                                              
timer := time.AfterFunc(time.Second, foo)       timer_t timer = timer_after(timer_second, foo());
timer.Stop()                                    timer_cancel(timer);

Github: https://github.com/shiyanhui/libcsp
文档: https://libcsp.com

查看原文

赞 7 收藏 6 评论 0

Yumiku 关注了专栏 · 2020-04-10

前端早早聊

技术经用得上、听得懂、抄得走,加微 codingdreamer 围观

关注 598

Yumiku 赞了文章 · 2020-04-10

职业思考:技术和管理路线之间如何取舍

著作权归作者所有。商业转载请联系 Scott 获得授权,非商业转载请注明出处[务必保留全文,勿做删减]。

程序员的青春饭,有人说是 30 岁,有人说是 35 岁,现实是这样么?真写不动的时候,要不要转,什么时候转,怎么转?

2018 年,刚好工作 8 年,也是我而立之年,在 30 岁这一年我从技术研发全面的转向了技术管理,经历了一个夜夜深思夜夜失眠的阶段,今天把我的思考结果分享给大家,包括身边的一些人一些事也会贯穿在本文之中,尝试给大家梳理一些选择之前需要考量的因素。

在开始之前,先送大家四句大白话:

  • 了解了解不停去了解
  • 规划规划不断做规划
  • 执行执行绝对要执行
  • 回馈回馈坚持做回馈

其实按照池老师的处事三原则就是:闭环、谁难受谁推进、Think Bigger,换做是我自己的方式,总结一下,就是不断去了解自己,不断去修正规划,不断的推动执行,不断上下左右回馈,如此反复再无它。

做技术到底是做什么

未来机器会取代我们,就像年轻的工程师会取代我们一样,凭借更佳的脑力运算凭借更低的应用成本,那我们当下做技术到底是做什么?

工程师赖以生存的技术,是驱动这个世界发生质变的核心推力引擎,它的本质是创新也就是再造,通过消耗我们的脑力去更好的消耗计算机的算力,脑力换算力成立的前提是脑力可以做更多意识层面逻辑性的情感性的决策,可以通过强大的抽象能力分析事物的本质,而算力目前还很难做到,等到机器可以做深度抽象做自主决策的时候,就是脑力编程下岗的时候,那时候就要依靠我们人类无穷无尽的想象力与创造力来输出规则给机器,机器帮我们做最优决策和过程实现。

回到当下,最优决策和过程实现还必须靠我们这一代人,而做技术就是在做决策和付诸实现,只不过我们凭借特定的语法规则给机器编写特定的执行任务,而语法掌握的熟悉度、技术运用的熟练度以及对机器和规则运行作用过程的理解程度决定了我们的技术实力是处在哪个段位哪个 Level。

Java 的语法有它特定的结构,PHP/Python/Javascript 统统不例外,这些特定的语法规则和结构我们都可以通过记忆烂熟于心,而它们背后的技术思想,基于它们衍生的特定框架,结合它们所形成的特定语言工具生态,对于这些的熟悉程度又进一步决定了我们运用技术的灵活程度,至于对机器和规则的理解则取决于我们训练大脑的深度和广度。

看到这里,我们再来看技术二字,就很容易明白它无非就是一些技巧规则的运用,而我们常年累月所浸泡的技术体系里面,经常面对的恰恰就是这些技巧和规则,编程经历越久,对于技巧掌握的越多,对于规则理解的越深刻,基于技巧和规则我们能衍生出的再造能力也更强,而通向更强再造能力的过程中,我们做技术的本质就是不断训练自己的大脑熟悉这些技术体系,建立新的认知体系,再创造出新的解决思路和应用方法论,所以到最底层的时候,比拼的还是想象力和创造力,只是很多时候我们空有想象力和创造力,却缺少让想法落地的技巧和规则运用能力,原因在于我们训练的还不够深刻,还不够丰富,也就是量变不够充分,质变也就很难发生,做技术本质就是 架构、编程和运维保障,架构就是训练出来的脑力逻辑性,编程就是规则和技巧,而运维保障则是规则应用的土壤,把它们拆解一下,就是下图上的一些技能点:

image.png

至于如何通过技术训练我们的大脑,实际上就是如何保持技术的快速成长和持续成长,我们前文有过相关的讨论,在这里我强调一点,技术需要持续性的时间投入来训练才能保持一定速度的成长,一旦投入中断,或者投入强度变小,很快技术成长就会降速,甚至没踩上行业趋势的话,会被同行甩在后面,作为这个小话题的结尾,我举一个身边的案例:

有一个同学 13 年毕业,进入大公司历练 2 年,技术趋于资深,后来高薪跳槽到一家创业公司成为核心骨干,创业公司发展迅速,1 年内就有了将近 10 人的前端团队,就任命这个同学成为技术 TL 带领团队,但快速膨胀的业务线迅速吃满了该同学的编程和架构时间,业务越来越多团队越来越大,他也就半推白就的走向了管理岗位,2015~2016 年起 React 进入蓬勃发展的时期,也是 Node 开始大规模应用的时期,他身不由己的松懈了对于技术深度的精进,到 2018 年时虽然薪资拿的较高,但原有的技术体系与当下的技术栈已不合时宜,编程的综合能力也快速下降,更重要的是,他内心依然向往编程,对于技术管理并没有建立太浓厚的兴趣也没有积淀太多成熟的管理方法论,后来痛定思痛,跳槽出来,降薪 30% 进入了一家公司做基础架构,技术重新捡起,降薪是因为他的技术能力已经低于市面行情所需求的实力,从 18 年到 19 年,他越来越开心,虽然薪资还没当年拿的多,但技术总算慢慢追赶上来了,所以,技术的精进需要持续的投入,如果中断就很快掉队,在切向管理的时候,一定要深思熟虑三思后行。

做管理到底是做什么

技术是以熟练的技巧和规则为基础,靠兴趣、想象力和创造力驱动,那技术管理是做什么呢,把管理丢开只做技术可不可以?答案是可以,答案同时也是不可以,可以还是不可以要分阶段来看,在职业的早期阶段是可以的,在走向资深的阶段后就不可以,因为管理不仅仅是以人为对象,包括自己包括组织,包括一件件复杂的事情和合作关系,都需要具备管理能力,为什么需要管理能力,是因为技术编程现在已经进入了深水区,光具备 编程、架构和运维实际上只是具备了基础能力而已:

image.png

抛开基础技能还需要更多其他的综合能力,假如业务走在最正确的方向,确定最合理正确的指标,运营收集到最客观真实的客户反馈,产品规划出最直击用户心灵最命中业务痛点的产品路线,产出最完整最闭环最精准的 PRD 文档,UED 同学设计出最符合用户需求最契合 PRD 文档的设计稿,服务端提供最稳定最明确的接口稳定和接口联调环境,项目经理把握最稳妥最可控的迭代节奏...在这样的环境下,有可能你能专心的写代码不需要跟各方有太多纠缠,可是试问下:这个 “最” 字有几个人可以做到,你经历过这样超高命中率超强配合能力的团队么?任何一个环节出岔子,就可能代码编程实现中的重大干扰因素,这还仅仅是对于一个人是如此,如果 10 个人前端团队,每个人都处在这种不稳定的状况下呢?

其实我们都处在一个个不完美的公司,一个个不完美的团队中,即便如 BAT AMD,又何尝没有道不完的心酸,团队越大问题越复杂,而作为技术管理者,职责就是解决团队中出现一切问题,并能促使团队不断的变得更优秀,前文我们讨论如何带领小团队时有张截图,这是小菜前端早期的一个阶段:

image.png

其实一个团队出问题的时候,是远不止上面这些问题的,当这些同时出现交叉出现的时候,团队的技术管理者应该做什么呢?答案就是发现问题、定义问题、制定方案、解决问题、复盘总结,发现问题是最容易做的事情,团队中大家的吐槽随时都是发现了问题,但定义问题则是一个更重要的能力,如何准确的定义问题直接决定了能否制定出正确的应对方案。

我们再把上面的其他综合能力做个分类归纳,会发现如下特征:

image.png

所有的这些能力集合,都可以成为管理岗需要的能力,只不过我们所认为是管理是比较狭义的管理能力而已,它的对象更多在于组织内部的向上向下和向左向右。

这时候我们也就意识到,管理所涵盖的的确不仅是事情,而是将所有能力综合后完整的推进路径,简言之就是驱动团队的方法论,方法论是可以训练的,但这些方法论往往跟编程本身没有关系,方法论投入精力越多,编程参与就越少,这就是技术向和管理向的天然矛盾,但方法论的背后反而有很多通用套路是对编程也是有益的,比如 PDCA 这样的做事规划模型,我对它做了改进,加入了意愿和能力的维度:

image.png

尽然技术路线与管理路线有矛盾也有统一,但最终它们通往的方向不同,我们在做职业转型的时候,就必然要取舍,就必然会经历内心的煎熬,就必然在某条路上愈行愈远。

管理能力一定是加分项

看完前面两个论述,我们会觉得管理好像不是一件很容易做的事,也不太像是一个对很工程师生涯很友好的选型,事实上,在整个市场的技术岗位稀缺度上,技术管理者与技术专家是同样的稀缺,无论这个同学技术是一般般还是非常好,身上具备管理能力一定是加分项。

因为技术团队是最好带也是最难带的,好带是因为技术同学的单纯与淳朴,难带是技术同学的天真和不成熟,一个好的技术管理者可以把团队打理的井井有条,发挥更大的效能,让业务方爽的不要不要的,团队成员也都能不断的获得成长,而一个不合格的管理者则会把团队搞的乌烟瘴气、怨声载道,甚至跟业务方无法建立信任关系,这对于一家企业是灾难性的后果。

因此用人方任命技术管理者,宁肯不求有多完美的果,却希望不要有太多的过,因为一旦技术团队与公司之间撕裂或者内部撕裂,整个公司将付出更巨大的资本和时间成本,来再次把团队校正到正确轨道,而由于技术团队是公司最贵重的资源,这个技术管理岗就至关重要,这就解释了为什么它是加分项。

最后一个观点,技术管理一定离不开技术二字,一个管理者可以不再精通技术,但要对技术有充分的经验积累和敏感度,要具备比较成熟的技术评估和技术人才运用的方法论,一定不可以抛开技术,纯粹从业务和人的视角做管理,比如用强 KPI 强指标的考核,或者只追求短期业务实现而不顾及长期基础设施建设和技术栈的迭代升级,那样对于技术团队的长期人才体系会有很大的伤害。

技术生涯跟天赋强相关

技术天赋越好的人,技术价值最大化的成功率越高,在技术的圈子里,技术成就能有多大,的确是因人而异,是有天赋因素加成的。

其实十几年看下来,真正在纯技术领域走向成功的人并不少,但前端这个领域却凤毛麟角,通常达到阿里的标准 P7 专家水平,就已经是大部分人的职业天花板了,还有少部分人能继续攀登到 P8 高级技术专家的高度,再往上走,就往往跟技术编程本身关系不大了,这也是为什么大部分人到了 P7,年龄也大都接近 30 岁上下,会慢慢转向管理,一个是市场需求大,一个是自己的职业天花板在这时候就遇到了,毕竟背后付出的心血都是脑力和身体。

所以,如果我们发现自己的技术天赋很高,那么可以尽量迟一点考虑管理,如果技术天赋一般,那么可以早一点理解管理的意义,并做一些必要的准备,将来迎接这个转型。

什么时候该考虑做管理

往往你做管理的时候,会源于一些外力,比如你跳槽去了一家新公司,刚好有这个管理岗坑位,比如你在本岗位干的风生水起,老板比较挺你让你带俩人试试,比如公司组织架构调整,你是被动任命,比如你所在团队老板离职,你临危受命等等,很多时候你从技术开始转向管理,都有一些机缘巧合,不知不觉就半只脚迈进了管理的池子中。

而你身上一定有了管理的标签,你就不再是一个人走,而是带着一群人走,你不再是对自己负责,而是对大家负责,你不再是仅仅面对业务,更是面向组织,可能就是这一夜之间,你的使命已经发生了根本性的跃迁,可是往往你要过很久才能慢慢明白过来。

这样的管理,其实是大部分人遇到的,也就是被动式的考虑,被动式的征召,而转向了管理,其实最好的管理路线,其实也是最好的技术路线,都跟我们前面提到的能力和意愿强相关,我们意愿上愿意转管理喜欢做管理,那么可以切换,如果意愿上极度排斥,那么就要深思而行,那怎么判断自己是喜欢还是排斥呢,我拿几张表格给大家测试下,看你喜不喜欢做这样的事情?

是否喜欢推导人的做事动机,是否愿意用这样的图来复盘自己和他人的项目过程,看所有人的能力和意愿处在哪个区间:

image.png

是否对商业模式感兴趣,愿意花时间研究不同行业不同公司不同团队,他们的成长情况,包括自己团队的成长阶段:

image.png

是否喜欢对问题进行分析归类,从人、事、组织等多个角度来抽象问题本质,并且有足够的热情去推动解决:

image.png

是否对于管理的手段,管理者的定位,以及所有公司决策层下发流程的执行力度有自己的分析,有更客观的评估:

image.png

是否真的愿意花时间去理解使命愿景价值观,人的道德品质和内心价值取向这些务虚的东西,是否认同人的驱动和需求层次与管理者胸怀及成就他人的关系:

image.png

以上这些事情与编程几乎无关,大家如果毫不排斥甚至喜欢,那么我认为当你的技术到达了技术专家的时候,或者再晚一点,都是可以考虑转管理的时候了,如果继续走技术路线,那么相信这些感兴趣的分析也会帮你更好更客观的分析所处的环境与所能发挥的价值。

小结

技术研发和技术管理有很多不同,但并不冲突,甚至在一个人的职业生涯中,二者的定位和比重也会来回切换,那是因为技术总是在急速的变革之中,只有积极的跟进技术浪潮,对技术保持较高的好奇心和敏感度,才能更好地做技术管理,同理在一直追求技术造诣和工程师价值的路上,也要时不时培养训练一些基础性的管理能力,至少是管理意识,这些软实力可以成为你当前岗位内推进各种疑难事项的润滑剂,同时也为将来的另一个职业路线打下一些基础,总之,技术路线与工程师创新价值的长远实现是贴合的,而技术管理与商业、团队和组织能力成长更贴合,两者在少数人身上可以很好的整合,形成软硬兼具能虚能实的综合竞争力,而我们大多数人,可以让自己在两个方面都不要有明显短板,才能在长期的职业生涯中,总能收获期许的回报。

Scott 近年面试或线下线上技术分享,遇到太多前端同学,由于团队原因/个人原因/职业成长/技术与管理通道,甚至家庭城市等等原因,在理想国与现实之间,在放弃与坚守之间,摇摆不停,心酸硬扛,大家可以找我聊聊南聊聊北,对工程师的宿命和价值有更多的看见与了解,Scott 微信: codingdream,也可以来关注 Scott 跟进我的动态

2.png
1.png

查看原文

赞 6 收藏 4 评论 0

Yumiku 收藏了文章 · 2020-04-08

为什么要用Go语言?

本文章创作于2020年4月,大约6000字,预计阅读时间15分钟,请坐和放宽。

logo.png

前言

Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易[1]。

Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了[1]。

其实早在2018年前,我就已经有在国内的程序员环境中断断续续地听到Go语言的消息,Go语言提供的方便的并发编程方式,十分适合我当时选择的毕业设计选题,但是受限于导师的语言选择、项目的进度追赶、考研的时间压榨,一直没有机会来好好地学习这门语言。

在进入研究生阶段后,尽管研究的方向和算法相关,但未来的职业方向还是选择了以后端为主,主要是因为想做更多和业务相关的工作。为了能在有限的时间里给予自己足够深的知识底蕴,选择了一些让自己去深入了解的方向,Go语言自然也在其中,今天终于有机会来开始研究这门语言。

为什么要用Go语言?

撰写此文的初衷,是本文的标题,也是我作为初学者一直以来的疑问:

“我为什么要用Go语言?”

为了回答这个问题,我翻阅了很多Go语言相关的文档、书籍和教程,我发现我很难在它们之中找到非常明显直接的答案,书上和教程只会说,“是的,Go语言好用”

对于部分人来说,这个问题的答案或许很“明显”,比如选择Go语言是因为Google设计的语言、Go开发赚的钱多、XX公司使用Go语言等等,如果想要了解这门语言更加本质的东西,仅仅这些答案我认为是还不够的。

部分Go的教徒可能会说,他们选择的理由是和语言本身相关的,比如:

  • Go编译快
  • Go执行快
  • Go并发编程方便
  • Go有垃圾回收(Garbage Collection, GC)

的确,Go是有这些特点,但这并非都是Go独有的

  • 运行时解释的脚本语言(比如Python)几乎不需要时间编译
  • C、C++甚至是汇编,基本上能够榨干一台机器的大部分性能
  • 大部分语言都有并发编程的支持库
  • 大部分语言都不需要程序员主动关注内存情况

一些Go的忠实粉丝把这种All in One的特性作为评价语言的标准,他们认为至少在这些方面,Go是可以完美的代替其他语言的。

那么,Go真的能优秀到完全替代另一个语言么?

其实未必,我始终认为银弹是不存在的[2],无论是在这次调查前,还是在这次调查后。

本文从Go语言被设计的初衷出发,深入互联网各种角落,调查Go所具有的那些特性是否足够优秀,同时和其他语言进行适当的比较,你可以选择性的阅读、接受或者反对我的内容,毕竟有交流才能传播知识。

我的最终目的是让更多的初学者看到Go没有轻易暴露出的缺点,同时也能看到Go真正优秀的地方

设计Go的初衷

Go语言的主要目标是将静态语言的安全性和高效性与动态语言的易开发性进行有机结合,达到完美平衡,从而使编程变得更加有乐趣,而不是在艰难抉择中痛苦前行[3]。

Google公司不可能无缘无故地设计一个新语言(一些特性相比于其他语言也没有新到哪里去),这一切肯定是有原因的。

设计Go语言是为了解决当时Google开发遇到的一些问题[4]:

  • C++编译慢、没有现代化(入门级友好的)的内存管理
  • 数以万计行的代码,难以维护
  • 部署的平台各式各样,交叉编译困难
  • ......

joke.png

找不到什么合适的语言,想着反正都是弄来自己用,Google选择造个轮子试试。

Go 语言起源 2007 年,并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20%兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 IT 工程师:Robert Griesemer,参与开发 Java HotSpot 虚拟机;Rob Pike,Go 语言项目总负责人,贝尔实验室 Unix 团队成员,参与的项目包括 Plan 9,Inferno 操作系统和 Limbo 编程语言;Ken Thompson,贝尔实验室 Unix 团队成员,C 语言、Unix 和 Plan 9 的创始人之一,与 Rob Pike 共同开发了 UTF-8 字符集规范。自 2008 年 1 月起,Ken Thompson 就开始研发一款以 C 语言为目标结果的编译器来拓展 Go 语言的设计思想[3]。

go-designers.png

Go 语言设计者:Griesemer、Thompson 和 Pike [3]

当时Google的很多工程师是用的都是C/C++,所以语法的设计上接近于C,Go的设计师们想要解决其他语言使用中的缺点,但是仍保留他们的优点[5]:

  • 静态类型和运行时效率
  • 可读性和易用性
  • 高性能的网络和多进程
  • ...

emmm,这些听起来还是比较玄乎,毕竟设计归设计,实现归实现,我们回顾一下现在Go的几个主要特点,编译速度、执行速度、内存管理以及并发编程。

Go的编译为什么快

当然,设计Go语言也不是完全从零开始,最初Go的团队尝试设计实现一个Go语言的编译前端,由基于C的gcc编译器来编译成机器代码,这个面向gcc的前端编译器也就是目前的Go编译器之一的gccgo。

与其说Go的编译为什么快,不如先说说C++的编译为什么慢,C++也可以用gcc编译,编译速度的大部分差异很有可能来源于语言设计本身。

在讨论问题之前,其中需要先说明的一点是:这里比较的编译速度都是在静态编译下的

静态编译和动态编译的区别:

  • 静态编译:编译器在编译可执行文件时,要把使用到的链接库提取出来,链接打包进可执行文件中,编译结果只有一个可执行文件。
  • 动态编译:可执行文件需要附带独立的库文件,不打包库到可执行文件中,减少可执行文件体积,在执行的时候再调用库即可。

两种方式有各自的优点和缺点,前者不需要去管理不同版本库的兼容性问题,后者可以减少内存和存储的占用(因为可以让不同程序共享同一个库),两种方式孰优孰弱,要对应到具体的工程问题上,Go默认的编译方式是静态编译

回到我们要讨论的问题:C++的编译为什么慢?

C++编译慢的主要两个大头原因[6]

  • 头文件的include方式
  • 模板的编译

C++使用include方式引用头文件,会让需要编译的代码有乘数级的增加,例如当同一个头文件被同一个项目下的N个文件include时,编译器会将头文件引入到每一份代码中,所以同一个头文件会被编译N次(这在大多数时候都是不必要的);C++使用的模板是为了支持泛型编程,在编写对不同类型的泛型函数时,可以提供很大的便利,但是这对于编译器来说,会增加非常多不必要的编译负担。

当然C++对这两个问题有很多后续的优化方法,但是这对于很多开发者来说,他们不想在这上面有过多时间和精力开销。

大部分后来的编程语言在引入文件的方式上,使用了import module来代替include 头文件的方式,import解决了重复编译的问题,当然Go也是使用的import方式;在模板的编译问题上,由于Go在设计理念上遵循从简入手,所以没有将泛函编程纳入到设计框架中,所以天生的没有模版编译带来的时间开销(没有泛型支持也是很多人不满Go语言的理由)。

在Go 的1.5 版本中,Go团队使用Go语言来编写Go语言的编译器(也叫自举),相比于gccgo来说:

  • 提高了编译速度,但执行速度略有下降(性能细节优化还不如gcc)
  • 增加了可编译的平台类型(以往受限于gcc)

在此之外,Go语言语法中的关键字也是非常少的(Go1.11版本里只有25个)[7],这也可以减少编译器花费在语法解析上的时间开销。

keywords.png

所以在我看来,Go编译速度快,主要出于四个原因

  • 使用了import的引用管理方式;
  • 没有模板的编译负担;
  • 1.5版本后的自举编译器优化;
  • 更少的关键字。

所以为了加快编译速度、放弃C++而转入Go的同时,也要考虑一下是否要放弃泛型编程的优点。

注:泛型可能在Go 2版本获得支持。

Go的实际性能如何

Go的执行速度,可以参考一个语言性能测试数据网站 —— The Computer Language Benchmarks Game[8]。

这个网站在不同的算法上对每个语言进行测试,然后给出时间和内存上的开销数据比对。

比较的语言有C++、Java、Python。

首先是时间开销:

time-cost.png

注意时间开销的单位是s,并且Y轴为了方便进行不同跨度上的比较,所以选取的是对数轴(即非线性轴,为1-10-100-1000的比较跨度)。

然后是内存开销:

mem-cost.png

注意Y轴为了方便进行不同跨度上的比较,所以选取的是对数轴(即非线性轴,为1000-10000-100000-1000000的比较跨度)。

需要注意的是,语言本身的性能只决定了一个程序的最高理论性能,程序具体的性能还要取决于这个程序的实现方法,所以当各个语言的性能并没有太大的差异时,性能往往只取决于程序实现的方式。

通过两个图的数据可以分析:

  • Go虽然还无法达到C++那样的极致性能,但是在大部分情况下已经很接近了
  • Go和Java在算法的时间开销上难分伯仲,但在内存的开销上Java就要高得多了;
  • Go在上述的绝大部分情况下,至少时间和内存开销都比Python要优秀得多;

Go的并发编程

Go的并发之所以比较受欢迎,网络上的很多内容集中在几个方面:

  • 天生并发的设计
  • 轻量化的并发编程方式
  • 较高的并发性能
  • 轻量级线程Goroutines、并发通信Channels以及其他便捷的并发同步控制工具

由于Go在设计的时候就考虑到了并发的支持,或者说很多特性都是为了并发而设计,这和一些后期库支持并发和第三方库支持并发的语言不同。

所以Go的并发到底有多方便?在Go中使用并发,只需要在普通的函数执行前加上一个go关键字,就可以新建一个线程让函数在其中执行:

func main() {
    go loop() // 启动一个goroutine
    loop()
}

这样带来的好处不仅仅是让并发编程更方便了,在一些特定情况下,比如Go引用一些使用了并发的库时,这些库所使用的并发也是基于Go本身的并发设计,不会存在库使用另一套并发实现的情况,这样Go调度器在处理程序中的各种并发线程时,可以有更加统一化的管理方式。

不过Go的并发对于程序的实现要求还是比较高的,在使用一些通信Channel的场合,稍有疏忽就可能出现死锁的问题,比如:

fatal error: all goroutines are asleep - deadlock!

Go的并发量可以比大部分语言里普通的线程实现要高,这受益于轻量级的Goroutine,轻量化主要是它所占用的空间要小得多,例如64位环境下的JVM,它会默认固定为每个线程分配1MB的线程栈空间,而Goroutines大概只有4-8KB,之后再按需分配。足够轻量化的线程在相同的内存下也就可以有更高并发量(服务器CPU还没有饱和的情况下),同时也可以减少很多上下文切换的时间开销[9]。但是如果你的每个线程占用空间都非常大时(比如10MB,当然这是非常规需求的情况下),Go的轻量化优势就没有那么明显了。

Go在并发上的优点很明显,也是Go的功能目标,从语言设计上支持了并发,提供了统一便捷的工具,复杂的并发业务也需要在Go的一整套并发规范体系下进行编程,当然这肯定会牺牲部分实现自由度,但可以获得性能的提高和维护成本的下降。

PS:关于Go调度器的内容在这里并没有被提及,因为很难用简单的文字向读者说明该调度方式和其他调度方式的优劣,将在未来的某一篇中会细致地介绍Go调度器的内容。

Go的垃圾回收

垃圾回收(英语:Garbage Collection,缩写为GC),在计算机科学中是一种自动的存储器管理机制。当一个计算机上的动态存储器不再需要时,就应该予以释放,以让出存储器,这种存储器资源管理,称为垃圾回收。垃圾回收器可以让程序员减轻许多负担,也减少程序员犯错的机会[10]。

在使用Go或者其他支持GC的语言时,不用再像C++一样,手动地去释放不需要的变量占用的内容空间(free/delete)

的确,这很方便(对于懒人和容易忘记主动释放的人),但是也多了一些限制(暗箱操作的不透明性以及在GC处理上的性能开销)。GC也不是万能的,当遇到一些对性能要求较高的场景,还是需要记得进行一些主动释放或优化操作(比如说自定义内存池)。

PS:将在未来的某一篇中会细致地介绍Go垃圾回收的细节(如果你们也觉得有必要的话)。

什么时候可以选择Go?

Go有很多优点,编译快、性能好、天生并发以及垃圾回收,很多比较有特色的内容也还没有说到(比如gofmt)。

Go语言也有很多缺点,比如第三方库支持还不够多(相比于Python来说就少的太多了)、支持编译的平台还不够广、还有被称为噩梦的依赖版本管理(已经在改善了,但是还没有达到完全可靠的程度)。

所以到底Go适合做什么,不适合做什么?

分析了这么多后,这个问题其实很难回答,但我们可以选择先从不适合的领域把Go剔除掉,看看我们会剩下什么。

Go不适合做什么

  • 极致高性能优化的场景,你可能需要使用C/C++,甚至是汇编;
  • 简单流程的脚本工具、数值分析、深度学习,可能Python更适合(至少目前是);
  • 搭一个博客或网站,PHP何尝不是天下第一的语言呢;
  • 如果你想比较方便找到一份的后端工作,绝大部分公司的Java岗一直缺人(在实际生产过程中,目前Go仍没有比Java表现得好太多,至少没有好到让一个部门/公司将核心业务重新转向Go来进行重构);
  • ...

你可以找到类似上面那样的很多场景,你可能会发现Go并不能那么完美地替代掉谁。

Go适合做什么

最后,到了我们的终极问题,Go到底适合做什么?

读到这里你可能会觉得,好像是我把Go的特性吹了一遍,然后突然告诉你可能Go不适合你。

Go天生并发,面向并发,所以Go的定位一直很清楚,从最浅显的视角来看,至少Go作为一个有较高性能的并发后端来说,是具有非常大的诱惑力的。

尤其对于后端相关的程序员而言,在某些业务功能的初步实现上,简洁的语法、内置的并发、快速的编译,都可以让你更加高效快速地完成任务(前提是Go的内容足以完成你的任务),不用再去担忧编译优化和内存回收、不用担心过多的时间和内存开销、不用担心不同版本库之间的冲突(静态编译)以及不用担心交叉编译平台适配问题。

大部分情况下,编写一个服务,你只需要:实现、编译、部署、运行

高效快速,足够敏捷,这在企业的绝大部分项目的初期都是适用的,这也是大部分项目对开发初期的要求。当一个项目或者服务真的可以发展下去,需求的确触碰到Go的天花板时,再考虑使用更加好的语言或方法去优化也为时不晚。

简而言之,尽管Go的过于简洁带来了很多问题(有些人说的难听点叫过于简单),Go所具有的优点,可以让大部分人用编程语言这种工具,来解决对他们而言更加重要的问题。

Go语言不是银弹,但它的确能有效地解决这些问题。

参考文章

扩展阅读

在调查Go的过程中,发现了一些比较有意思、或者比较实用的文章,一并附在这里。

  • 我为什么选择使用 Go 语言?,该文写于2016年,在我的文章基本构思完成的时候,偶然看到了这篇文章,作者有很多早期Go版本的开发经验,里面有更多的细节都是出自于工程师的经验之谈,我发现其中的部分想法和我不谋而合,你可以把这篇文章当作本文的后续扩展阅读,不过要注意文章的时效,可能提及到的一些Go的缺点现在已经被改进了。
  • C/C++编译器的工作过程,主要是供不熟悉C系的朋友了解一下编译器的工作过程。
  • The Computer Language Benchmarks Game,一个对各个语言进行性能测试的网站,里面的算法具有一定的代表性,但是不能代表所有工程可能遇到的情况,仅供参考。
  • 为什么 Go 语言在某些方面的性能还不如 Java?,这是知乎上一个2017年开始有的问题,你可以看到很多人对于这个问题的分析,从多个角度来理解语言之间的性能差异。
  • go-wiki WhyGo,Go的Github仓库上维护的Wiki中,有一篇关于WhyGo的文章整理,不过大部分是英文,里面主要是很多关于“为什么我要选择Go”的软硬稿。
  • 为什么要使用Go语言,Go语言的优势在哪里,这个知乎的提问更早,是来自2013年的Yvonne YU用户,在Go的早期其实是具有很大的争议的,你可以看到大家在各个问题上的博弈。
  • 哪些公司在使用Go,Go的Github仓库上维护的Wiki中,有一篇关于全球都有哪些公司在使用Go,不过提供的信息大部分只有一个公司名,比如国内有阿里巴巴(而人家大部分都招Java),可以看看但参考性不大。
  • Go 语言的优点,缺点和令人厌恶的设计,这是Go语言中文网上一篇2018年的文章,如果你对语言本身的一些特性的设计感兴趣,你可以选择看看,作者从很多语法层面上介绍了Go的优点和缺点。
  • Ruby China - 瞎扯淡 真的没必要浪费心思在 Go 语言上,这是我无意中找到的一篇有名的帖子,这个问题始于2013年,在Ruby China上,其中也是大佬们(可能)从各个角度来辩论Go是否值得学习,可以当作武侠小说观看。
  • The way to Go - 3.8 Go性能说明,《The way to Go》这本书上为数不多关于Go性能问题的说明。
  • C++开发转向go开发是否是一个好的发展方向?,2014年知乎上关于C++和Go的一个讨论,其实我觉得“如果选择一个并不意味着就要放弃另一个”,程序员不是研究语言的,也不应该是只靠某一门语言吃饭。
  • 我为什么放弃Go语言 Liigo,嗯,2014年,仍旧是Go争议很大的时候,CSDN上一篇阅读数很高的文章,作者从自己的角度对Go进行批判(Go早期的确是有不少问题),你可以看到早期Go的很多问题,也可以斟酌这些问题对你是否重要以及到底在2020年的Go中有没有被解决。
  • Golang 本身是用什么语言写的?,一个关于编译的有趣的问题,可以适当了解。
  • 搞懂Go垃圾回收,一篇还算比较新的分析Go垃圾回收问题的文章。
  • 有趣的编程语言:Go 语言的启动时间是 C 语言的 300 多倍,C# 的关键字最多,这篇InfoQ文章其实算是一个典型的标题党,主要使用的是一个Github上关于各个语言HelloWorld程序启动时间的测试数据(https://github.com/bdrung/sta...,使用gccgo编译的Go程序的启动时间非常地长,的确是C的300多倍,但使用GC编译的Go程序启动时间只是C的2倍。
  • Go 语言的历史回顾,我一直在寻找一个整理Go的版本变动细节的文章,在Go的官方文档和各种书籍上寻找无果时,在InfoQ上找到了一篇还算跟踪地比较新的(Go 1.0 - Go 1.13)文章,对于初学者而言,知道语言的变化也是很重要的(比如方便的知道哪些问题解决了,哪些还没有被解决),可能之后会拓展性的写一篇关于这个的文章。
查看原文

Yumiku 发布了文章 · 2020-04-07

为什么要用Go语言?

本文章创作于2020年4月,大约6000字,预计阅读时间15分钟,请坐和放宽。

logo.png

前言

Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易[1]。

Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了[1]。

其实早在2018年前,我就已经有在国内的程序员环境中断断续续地听到Go语言的消息,Go语言提供的方便的并发编程方式,十分适合我当时选择的毕业设计选题,但是受限于导师的语言选择、项目的进度追赶、考研的时间压榨,一直没有机会来好好地学习这门语言。

在进入研究生阶段后,尽管研究的方向和算法相关,但未来的职业方向还是选择了以后端为主,主要是因为想做更多和业务相关的工作。为了能在有限的时间里给予自己足够深的知识底蕴,选择了一些让自己去深入了解的方向,Go语言自然也在其中,今天终于有机会来开始研究这门语言。

为什么要用Go语言?

撰写此文的初衷,是本文的标题,也是我作为初学者一直以来的疑问:

“我为什么要用Go语言?”

为了回答这个问题,我翻阅了很多Go语言相关的文档、书籍和教程,我发现我很难在它们之中找到非常明显直接的答案,书上和教程只会说,“是的,Go语言好用”

对于部分人来说,这个问题的答案或许很“明显”,比如选择Go语言是因为Google设计的语言、Go开发赚的钱多、XX公司使用Go语言等等,如果想要了解这门语言更加本质的东西,仅仅这些答案我认为是还不够的。

部分Go的教徒可能会说,他们选择的理由是和语言本身相关的,比如:

  • Go编译快
  • Go执行快
  • Go并发编程方便
  • Go有垃圾回收(Garbage Collection, GC)

的确,Go是有这些特点,但这并非都是Go独有的

  • 运行时解释的脚本语言(比如Python)几乎不需要时间编译
  • C、C++甚至是汇编,基本上能够榨干一台机器的大部分性能
  • 大部分语言都有并发编程的支持库
  • 大部分语言都不需要程序员主动关注内存情况

一些Go的忠实粉丝把这种All in One的特性作为评价语言的标准,他们认为至少在这些方面,Go是可以完美的代替其他语言的。

那么,Go真的能优秀到完全替代另一个语言么?

其实未必,我始终认为银弹是不存在的[2],无论是在这次调查前,还是在这次调查后。

本文从Go语言被设计的初衷出发,深入互联网各种角落,调查Go所具有的那些特性是否足够优秀,同时和其他语言进行适当的比较,你可以选择性的阅读、接受或者反对我的内容,毕竟有交流才能传播知识。

我的最终目的是让更多的初学者看到Go没有轻易暴露出的缺点,同时也能看到Go真正优秀的地方

设计Go的初衷

Go语言的主要目标是将静态语言的安全性和高效性与动态语言的易开发性进行有机结合,达到完美平衡,从而使编程变得更加有乐趣,而不是在艰难抉择中痛苦前行[3]。

Google公司不可能无缘无故地设计一个新语言(一些特性相比于其他语言也没有新到哪里去),这一切肯定是有原因的。

设计Go语言是为了解决当时Google开发遇到的一些问题[4]:

  • C++编译慢、没有现代化(入门级友好的)的内存管理
  • 数以万计行的代码,难以维护
  • 部署的平台各式各样,交叉编译困难
  • ......

joke.png

找不到什么合适的语言,想着反正都是弄来自己用,Google选择造个轮子试试。

Go 语言起源 2007 年,并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20%兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 IT 工程师:Robert Griesemer,参与开发 Java HotSpot 虚拟机;Rob Pike,Go 语言项目总负责人,贝尔实验室 Unix 团队成员,参与的项目包括 Plan 9,Inferno 操作系统和 Limbo 编程语言;Ken Thompson,贝尔实验室 Unix 团队成员,C 语言、Unix 和 Plan 9 的创始人之一,与 Rob Pike 共同开发了 UTF-8 字符集规范。自 2008 年 1 月起,Ken Thompson 就开始研发一款以 C 语言为目标结果的编译器来拓展 Go 语言的设计思想[3]。

go-designers.png

Go 语言设计者:Griesemer、Thompson 和 Pike [3]

当时Google的很多工程师是用的都是C/C++,所以语法的设计上接近于C,Go的设计师们想要解决其他语言使用中的缺点,但是仍保留他们的优点[5]:

  • 静态类型和运行时效率
  • 可读性和易用性
  • 高性能的网络和多进程
  • ...

emmm,这些听起来还是比较玄乎,毕竟设计归设计,实现归实现,我们回顾一下现在Go的几个主要特点,编译速度、执行速度、内存管理以及并发编程。

Go的编译为什么快

当然,设计Go语言也不是完全从零开始,最初Go的团队尝试设计实现一个Go语言的编译前端,由基于C的gcc编译器来编译成机器代码,这个面向gcc的前端编译器也就是目前的Go编译器之一的gccgo。

与其说Go的编译为什么快,不如先说说C++的编译为什么慢,C++也可以用gcc编译,编译速度的大部分差异很有可能来源于语言设计本身。

在讨论问题之前,其中需要先说明的一点是:这里比较的编译速度都是在静态编译下的

静态编译和动态编译的区别:

  • 静态编译:编译器在编译可执行文件时,要把使用到的链接库提取出来,链接打包进可执行文件中,编译结果只有一个可执行文件。
  • 动态编译:可执行文件需要附带独立的库文件,不打包库到可执行文件中,减少可执行文件体积,在执行的时候再调用库即可。

两种方式有各自的优点和缺点,前者不需要去管理不同版本库的兼容性问题,后者可以减少内存和存储的占用(因为可以让不同程序共享同一个库),两种方式孰优孰弱,要对应到具体的工程问题上,Go默认的编译方式是静态编译

回到我们要讨论的问题:C++的编译为什么慢?

C++编译慢的主要两个大头原因[6]

  • 头文件的include方式
  • 模板的编译

C++使用include方式引用头文件,会让需要编译的代码有乘数级的增加,例如当同一个头文件被同一个项目下的N个文件include时,编译器会将头文件引入到每一份代码中,所以同一个头文件会被编译N次(这在大多数时候都是不必要的);C++使用的模板是为了支持泛型编程,在编写对不同类型的泛型函数时,可以提供很大的便利,但是这对于编译器来说,会增加非常多不必要的编译负担。

当然C++对这两个问题有很多后续的优化方法,但是这对于很多开发者来说,他们不想在这上面有过多时间和精力开销。

大部分后来的编程语言在引入文件的方式上,使用了import module来代替include 头文件的方式,import解决了重复编译的问题,当然Go也是使用的import方式;在模板的编译问题上,由于Go在设计理念上遵循从简入手,所以没有将泛函编程纳入到设计框架中,所以天生的没有模版编译带来的时间开销(没有泛型支持也是很多人不满Go语言的理由)。

在Go 的1.5 版本中,Go团队使用Go语言来编写Go语言的编译器(也叫自举),相比于gccgo来说:

  • 提高了编译速度,但执行速度略有下降(性能细节优化还不如gcc)
  • 增加了可编译的平台类型(以往受限于gcc)

在此之外,Go语言语法中的关键字也是非常少的(Go1.11版本里只有25个)[7],这也可以减少编译器花费在语法解析上的时间开销。

keywords.png

所以在我看来,Go编译速度快,主要出于四个原因

  • 使用了import的引用管理方式;
  • 没有模板的编译负担;
  • 1.5版本后的自举编译器优化;
  • 更少的关键字。

所以为了加快编译速度、放弃C++而转入Go的同时,也要考虑一下是否要放弃泛型编程的优点。

注:泛型可能在Go 2版本获得支持。

Go的实际性能如何

Go的执行速度,可以参考一个语言性能测试数据网站 —— The Computer Language Benchmarks Game[8]。

这个网站在不同的算法上对每个语言进行测试,然后给出时间和内存上的开销数据比对。

比较的语言有C++、Java、Python。

首先是时间开销:

time-cost.png

注意时间开销的单位是s,并且Y轴为了方便进行不同跨度上的比较,所以选取的是对数轴(即非线性轴,为1-10-100-1000的比较跨度)。

然后是内存开销:

mem-cost.png

注意Y轴为了方便进行不同跨度上的比较,所以选取的是对数轴(即非线性轴,为1000-10000-100000-1000000的比较跨度)。

需要注意的是,语言本身的性能只决定了一个程序的最高理论性能,程序具体的性能还要取决于这个程序的实现方法,所以当各个语言的性能并没有太大的差异时,性能往往只取决于程序实现的方式。

通过两个图的数据可以分析:

  • Go虽然还无法达到C++那样的极致性能,但是在大部分情况下已经很接近了
  • Go和Java在算法的时间开销上难分伯仲,但在内存的开销上Java就要高得多了;
  • Go在上述的绝大部分情况下,至少时间和内存开销都比Python要优秀得多;

Go的并发编程

Go的并发之所以比较受欢迎,网络上的很多内容集中在几个方面:

  • 天生并发的设计
  • 轻量化的并发编程方式
  • 较高的并发性能
  • 轻量级线程Goroutines、并发通信Channels以及其他便捷的并发同步控制工具

由于Go在设计的时候就考虑到了并发的支持,或者说很多特性都是为了并发而设计,这和一些后期库支持并发和第三方库支持并发的语言不同。

所以Go的并发到底有多方便?在Go中使用并发,只需要在普通的函数执行前加上一个go关键字,就可以新建一个线程让函数在其中执行:

func main() {
    go loop() // 启动一个goroutine
    loop()
}

这样带来的好处不仅仅是让并发编程更方便了,在一些特定情况下,比如Go引用一些使用了并发的库时,这些库所使用的并发也是基于Go本身的并发设计,不会存在库使用另一套并发实现的情况,这样Go调度器在处理程序中的各种并发线程时,可以有更加统一化的管理方式。

不过Go的并发对于程序的实现要求还是比较高的,在使用一些通信Channel的场合,稍有疏忽就可能出现死锁的问题,比如:

fatal error: all goroutines are asleep - deadlock!

Go的并发量可以比大部分语言里普通的线程实现要高,这受益于轻量级的Goroutine,轻量化主要是它所占用的空间要小得多,例如64位环境下的JVM,它会默认固定为每个线程分配1MB的线程栈空间,而Goroutines大概只有4-8KB,之后再按需分配。足够轻量化的线程在相同的内存下也就可以有更高并发量(服务器CPU还没有饱和的情况下),同时也可以减少很多上下文切换的时间开销[9]。但是如果你的每个线程占用空间都非常大时(比如10MB,当然这是非常规需求的情况下),Go的轻量化优势就没有那么明显了。

Go在并发上的优点很明显,也是Go的功能目标,从语言设计上支持了并发,提供了统一便捷的工具,复杂的并发业务也需要在Go的一整套并发规范体系下进行编程,当然这肯定会牺牲部分实现自由度,但可以获得性能的提高和维护成本的下降。

PS:关于Go调度器的内容在这里并没有被提及,因为很难用简单的文字向读者说明该调度方式和其他调度方式的优劣,将在未来的某一篇中会细致地介绍Go调度器的内容。

Go的垃圾回收

垃圾回收(英语:Garbage Collection,缩写为GC),在计算机科学中是一种自动的存储器管理机制。当一个计算机上的动态存储器不再需要时,就应该予以释放,以让出存储器,这种存储器资源管理,称为垃圾回收。垃圾回收器可以让程序员减轻许多负担,也减少程序员犯错的机会[10]。

在使用Go或者其他支持GC的语言时,不用再像C++一样,手动地去释放不需要的变量占用的内容空间(free/delete)

的确,这很方便(对于懒人和容易忘记主动释放的人),但是也多了一些限制(暗箱操作的不透明性以及在GC处理上的性能开销)。GC也不是万能的,当遇到一些对性能要求较高的场景,还是需要记得进行一些主动释放或优化操作(比如说自定义内存池)。

PS:将在未来的某一篇中会细致地介绍Go垃圾回收的细节(如果你们也觉得有必要的话)。

什么时候可以选择Go?

Go有很多优点,编译快、性能好、天生并发以及垃圾回收,很多比较有特色的内容也还没有说到(比如gofmt)。

Go语言也有很多缺点,比如第三方库支持还不够多(相比于Python来说就少的太多了)、支持编译的平台还不够广、还有被称为噩梦的依赖版本管理(已经在改善了,但是还没有达到完全可靠的程度)。

所以到底Go适合做什么,不适合做什么?

分析了这么多后,这个问题其实很难回答,但我们可以选择先从不适合的领域把Go剔除掉,看看我们会剩下什么。

Go不适合做什么

  • 极致高性能优化的场景,你可能需要使用C/C++,甚至是汇编;
  • 简单流程的脚本工具、数值分析、深度学习,可能Python更适合(至少目前是);
  • 搭一个博客或网站,PHP何尝不是天下第一的语言呢;
  • 如果你想比较方便找到一份的后端工作,绝大部分公司的Java岗一直缺人(在实际生产过程中,目前Go仍没有比Java表现得好太多,至少没有好到让一个部门/公司将核心业务重新转向Go来进行重构);
  • ...

你可以找到类似上面那样的很多场景,你可能会发现Go并不能那么完美地替代掉谁。

Go适合做什么

最后,到了我们的终极问题,Go到底适合做什么?

读到这里你可能会觉得,好像是我把Go的特性吹了一遍,然后突然告诉你可能Go不适合你。

Go天生并发,面向并发,所以Go的定位一直很清楚,从最浅显的视角来看,至少Go作为一个有较高性能的并发后端来说,是具有非常大的诱惑力的。

尤其对于后端相关的程序员而言,在某些业务功能的初步实现上,简洁的语法、内置的并发、快速的编译,都可以让你更加高效快速地完成任务(前提是Go的内容足以完成你的任务),不用再去担忧编译优化和内存回收、不用担心过多的时间和内存开销、不用担心不同版本库之间的冲突(静态编译)以及不用担心交叉编译平台适配问题。

大部分情况下,编写一个服务,你只需要:实现、编译、部署、运行

高效快速,足够敏捷,这在企业的绝大部分项目的初期都是适用的,这也是大部分项目对开发初期的要求。当一个项目或者服务真的可以发展下去,需求的确触碰到Go的天花板时,再考虑使用更加好的语言或方法去优化也为时不晚。

简而言之,尽管Go的过于简洁带来了很多问题(有些人说的难听点叫过于简单),Go所具有的优点,可以让大部分人用编程语言这种工具,来解决对他们而言更加重要的问题。

Go语言不是银弹,但它的确能有效地解决这些问题。

参考文章

扩展阅读

在调查Go的过程中,发现了一些比较有意思、或者比较实用的文章,一并附在这里。

  • 我为什么选择使用 Go 语言?,该文写于2016年,在我的文章基本构思完成的时候,偶然看到了这篇文章,作者有很多早期Go版本的开发经验,里面有更多的细节都是出自于工程师的经验之谈,我发现其中的部分想法和我不谋而合,你可以把这篇文章当作本文的后续扩展阅读,不过要注意文章的时效,可能提及到的一些Go的缺点现在已经被改进了。
  • C/C++编译器的工作过程,主要是供不熟悉C系的朋友了解一下编译器的工作过程。
  • The Computer Language Benchmarks Game,一个对各个语言进行性能测试的网站,里面的算法具有一定的代表性,但是不能代表所有工程可能遇到的情况,仅供参考。
  • 为什么 Go 语言在某些方面的性能还不如 Java?,这是知乎上一个2017年开始有的问题,你可以看到很多人对于这个问题的分析,从多个角度来理解语言之间的性能差异。
  • go-wiki WhyGo,Go的Github仓库上维护的Wiki中,有一篇关于WhyGo的文章整理,不过大部分是英文,里面主要是很多关于“为什么我要选择Go”的软硬稿。
  • 为什么要使用Go语言,Go语言的优势在哪里,这个知乎的提问更早,是来自2013年的Yvonne YU用户,在Go的早期其实是具有很大的争议的,你可以看到大家在各个问题上的博弈。
  • 哪些公司在使用Go,Go的Github仓库上维护的Wiki中,有一篇关于全球都有哪些公司在使用Go,不过提供的信息大部分只有一个公司名,比如国内有阿里巴巴(而人家大部分都招Java),可以看看但参考性不大。
  • Go 语言的优点,缺点和令人厌恶的设计,这是Go语言中文网上一篇2018年的文章,如果你对语言本身的一些特性的设计感兴趣,你可以选择看看,作者从很多语法层面上介绍了Go的优点和缺点。
  • Ruby China - 瞎扯淡 真的没必要浪费心思在 Go 语言上,这是我无意中找到的一篇有名的帖子,这个问题始于2013年,在Ruby China上,其中也是大佬们(可能)从各个角度来辩论Go是否值得学习,可以当作武侠小说观看。
  • The way to Go - 3.8 Go性能说明,《The way to Go》这本书上为数不多关于Go性能问题的说明。
  • C++开发转向go开发是否是一个好的发展方向?,2014年知乎上关于C++和Go的一个讨论,其实我觉得“如果选择一个并不意味着就要放弃另一个”,程序员不是研究语言的,也不应该是只靠某一门语言吃饭。
  • 我为什么放弃Go语言 Liigo,嗯,2014年,仍旧是Go争议很大的时候,CSDN上一篇阅读数很高的文章,作者从自己的角度对Go进行批判(Go早期的确是有不少问题),你可以看到早期Go的很多问题,也可以斟酌这些问题对你是否重要以及到底在2020年的Go中有没有被解决。
  • Golang 本身是用什么语言写的?,一个关于编译的有趣的问题,可以适当了解。
  • 搞懂Go垃圾回收,一篇还算比较新的分析Go垃圾回收问题的文章。
  • 有趣的编程语言:Go 语言的启动时间是 C 语言的 300 多倍,C# 的关键字最多,这篇InfoQ文章其实算是一个典型的标题党,主要使用的是一个Github上关于各个语言HelloWorld程序启动时间的测试数据(https://github.com/bdrung/sta...,使用gccgo编译的Go程序的启动时间非常地长,的确是C的300多倍,但使用GC编译的Go程序启动时间只是C的2倍。
  • Go 语言的历史回顾,我一直在寻找一个整理Go的版本变动细节的文章,在Go的官方文档和各种书籍上寻找无果时,在InfoQ上找到了一篇还算跟踪地比较新的(Go 1.0 - Go 1.13)文章,对于初学者而言,知道语言的变化也是很重要的(比如方便的知道哪些问题解决了,哪些还没有被解决),可能之后会拓展性的写一篇关于这个的文章。
查看原文

赞 20 收藏 9 评论 3

Yumiku 关注了标签 · 2020-03-08

docker

an open source project to pack, ship and run any application as a lightweight container ! By Lock !

关注 35879

Yumiku 关注了标签 · 2020-03-08

flutter

clipboard.png

Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK。

Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.

Flutter 官网:https://flutter.dev/
Flutter 中文资源:https://flutter-io.cn/
Flutter Github:https://github.com/flutter/fl...

关注 971

Yumiku 关注了标签 · 2020-03-08

golang

Go语言是谷歌2009发布的第二款开源编程语言。Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。
Go语言是谷歌推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发Go,是因为过去10多年间软件开发的难度令人沮丧。Go是谷歌2009发布的第二款编程语言。

七牛云存储CEO许式伟出版《Go语言编程
go语言翻译项目 http://code.google.com/p/gola...
《go编程导读》 http://code.google.com/p/ac-m...
golang的官方文档 http://golang.org/doc/docs.html
golang windows上安装 http://code.google.com/p/gomi...

关注 26108

Yumiku 关注了标签 · 2020-03-08

关注 505

认证与成就

  • 获得 25 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • 邻书

    一个二手书信息发布平台。

注册于 2017-10-31
个人主页被 894 人浏览