转载“手把手教你阅读开源代码”

一、开源项目初体验

1、走读一遍官网介绍文档(走读)

有多少人愿意花时间好好看官网文档的?估计很少,大家更倾向于了解个一知半解后就开始撸代码,强撸!!!

撸代码过程中会遇到很多稀奇古怪的问题,再回过头用搜索引擎来解决这些问题,发现是自己的使用方式有问题,其实某个api的使用方法在demo中或者文档中,已明明白白的写的很清楚。你说,是不是大冤种?

重点看文档中什么内容:

1、开源项目背景

采用了什么技术?

提供了什么功能?

解决了什么问题(或者核心痛点)?

和同类产品相比有哪些优势?

2、适用场景

优点是什么?

缺点是什么?

适用于什么场景?

不适用什么场景?

明确了适用场景,才能在工作中恰当的使用出来。

3、哪些公司在生产中使用

是否有中型以上公司在生产环境中去实践了此开源项目,可以从侧面证明这个开源项目很有潜力和竞争力,很值得你花费大量的时间和精力去研究。

比如使用brpc的公司如下:

4、核心概念和主体架构组成

相信很多小伙伴看工作内容相关的开源项目能阅读下去,但是其他领域的开源项目看不懂,并不是因为你能力不够,而是缺乏领域知识。

在已有的知识体系之下去学习新的开源项目,需要对新概念新技术有个大致的了解,才能更好的理解项目的整体实现思路,阅读起来源代码才事半功倍。

比如阅读lvs源代码,起码要明白以下概念

RS: Real Server【后端真实服务器】

Client IP: CIP【远程访问的客户端ip】

Director Virtual IP:VIP【用于对外公布的虚拟ip】

NAT、FULLNAT、DR、TUN模式。

千万不要一上来就看代码,核心概念不清楚,核心原理不懂,核心算法没吃透,看代码很难看懂。

5、尽量看官网英文的资料(时效和准确性更好)

尽量看官网英文的资料,从时效性和内容准确性上都要比中文技术博客的内容要好的多。

网上的博客,尤其是csdn的博客,大家都是抄来抄去,难免有错误和纰漏。

重点关注下Get Started和Example之类的文档,能够帮助更快的入门。

2、了解代码目录结构

在下载源代码之后,先看下代码目录组织结构,可先从示例代码或测试代码入手。

如base代表基础库,net代表网络库,

demo/example:示例代码,样本程序

tests:测试代码

docs:文档目录(类图,流程图,活动图,业务知识相关资料等)

入手之初,建议从例子代码开始看(demo,example,test),从而熟悉开源项目的流程或者使用方法。

对于不理解的技术点,要认真的查询文档来验证其准确性,技术是一门容不得马虎的手艺。

3、相关视频或ppt

文档和代码毕竟是静态资源,能传达的信息不够生动形象,如果能有相关的技术视频搭配来一起看,效果就好的多了。

4、看cmake或makefile构建脚本

github上流行的开源项目,比如C/C++/Go语言相关的项目,多数还是以cmake或makefile体系来构建的。

代码在构建时,有以下讲究:

1、查看依赖库有哪些?

2、开启或关闭指定功能的编译宏,传递一些宏到代码中

3、编译可Debug的程序版本,方便后续Debug调试

4、熟悉开源代码的构建脚本,才能方便改代码,编译,调试的后续流程

二、安装部署,运行(很重要!)

好的开源项目,其文档都比较完备,安装部署文档一般会在README中或者doc目录中,一般会提供几种部署方式

2、1 yum或apt源安装

2、2 rpm或deb包安装

2、3 源代码编译安装部署

2、4 docker镜像部署

将程序运行起来!!!

将程序运行起来!!!

将程序运行起来!!!

重要的事说三遍,程序运行起来有,我们就有了直观感受。

就我的经验而言,一个项目代码,是否能顺利的搭建调试环境,效率大不一样。

跑起来之后,还要尽量的精简自己的环境,减少调试过程中的干扰信息。比如,Nginx使用多进程的方式处理请求,为了调试跟踪Nginx的行为,我经常把worker数量设置为1个,这样调试的时候就知道待跟踪的是哪个进程了。

也就是,为了方便调试,我们会修改开源项目的代码。

玩起来,嗨起来,搭建成功后必须装逼起来,哈哈

首先是便于从视觉感官上体会项目的功能,

其次方便下断点调试,修改代码,再调试,查看是否符合修改的预期;

最后,方便截图,截牛逼的图去吹牛逼,2333~

三、运行简单Example例子

example目录:例子目录,先通过例子上手学会如何使用此开源项目

3、1 直观感受程序功能

安装部署好项目,最好能有web界面等交互式界面或者有数据结果输出,从宏观上熟悉了开源项目的大体功能。

然后从微观上入手,转到局部的example例子上,扣细节。

阅读简单的入门例子,能够抛开复杂的处理细节和高级特性,便于尽快熟悉项目的主干和核心模块。

3、2 由浅入深剖析例子代码

例子中根据api的难易,分为初级、中级、高级,我们可以先入门初级的例子,再熟悉基础流程中,可以考虑深入研究下中级或高级的例子,从而写出更贴合公司业务逻辑的代码。

简单例子都遇到问题,可用邮件列表、社区、Issue来解决遇到的问题。

3、3 记录心中疑惑,带着问题阅读

熟悉简单例子的过程中,对于有疑问的地方先记录下问题,后面带着问题去阅读源代码解决心中疑惑。

熟悉简单例子后,也可阅读些高级点的例子,或手动修改代码看看例子是否能正常运行?

如果公司使用的开源项目实在没有example例子,可考虑自己根据公司需求编写一个demo例子

3、4 编写符合公司场景的demo代码

看懂demo或example代码,仅能说明熟悉了api的使用流程和功能边界,但是想要真正吸收开源代码的知识变成自己的知识,需要根据公司场景动手敲代码写个demo,并对此demo做功能性验证,期间遇到各种问题并解决问题,会增长对于开源项目的技术理解。

四、认真细致的看文档,看单元测试用例

4、1 看代码之前认真看文档

一个好的开源项目是有完善的文档的,比如百度开源的RPC框架brpc

docs目录:

库的开发入门指南,代码的风格文档,程序的命令操作手册,程序中采用的第三方库

类图,流程图,活动图,架构图,设计文档

业务相关基础知识介绍文档

项目技术细节详细介绍

阅读这些文档可以了解该项目的大体设计和结构,读源码的时候不会无从下手。

务必要耐心好好的看,多看多实践多思考,少BB

4、2 认真看测试用例

如果测试用例写的很仔细,那么很值得好好去研究一下。

原因在于:测试用例往往是针对某个单一的场景,独自构造出一些数据来对程序的流程进行验证。

test目录:测试目录,学习下单元测试是如何写的

通过看单元测试用例,可了解到某个类或某个组件是如何使用的

五、尽情的玩耍

各种玩:

执行example中的例子查看效果,也可以修改example中的例子来验证自己的想法。

修改test目录中的代码,看看测试代码是否能通过

比如nDPI项目,开始玩时,可以只加载一个插件,玩熟练后可以加载N个插件;

开源项目配置文件也有很多可以玩的地方,尽情的玩,但是千万别在线上环境玩!!!!

先学会如何使用,然后再去从源代码角度 理解实现原理。

六、阅读源代码

6、1 明确阅读代码的目的(推荐带着问题读)

在开始阅读代码之前,首先要明确自己的阅读目的:

是需要了解其中一个模块的实现?比如基础模块还是业务模块?基础模块中有内存池、线程池、网络数据收发epoll模型等

还是需要了解这个框架的大体结构,

还是需要具体熟悉其中的一个算法的实现,等等。

心里很清楚自己想要什么,才能更有动力去朝着目标努力奋斗。

6、2 区分主线和支线剧情

阅读源代码时,分清主线和支线

先看主线:

想了解一个业务逻辑的实现流程,中间调用了第三方库函数、utils函数、定制的数据结构和算法等,其实不需要了解其内部实现;

只需要了解其对外接口,了解这些接口的入口、出口参数以及作用,把这部分当成一个“黑盒”即可。

再看支线:

在对主线有充分了解前提下,可以考虑打开“黑盒”研究下其内部实现,比如看看内存池的实现代码,调度器的代码,dpdk中对于海量数据包是如何处理的等。

具体做法:

从main函数进入,使用gdb单步跟踪理清一次完整流程(如程序初始化)的代码调用路径,这可以通过debug来观察运行时的变量和行为。

6、3 挑选感兴趣的“分支”来阅读

这里的分支指的是功能或版本:

从功能上说:

比如你对网络通讯感兴趣,就阅读网络层的代码,深入到实现细节:

如它用了什么库,

采用了什么设计模式,

为什么这样做等。

如果可以,debug细节代码。

从源代码版本上说:

不一定选择最新版本的开源代码进行阅读,如果感觉有点吃力,可选用同一个开源项目的老版本(如1.0版本),

此时的项目代码,不管是代码量还是复杂度上都要小很多,看起来要容易的多。

6、4 理清核心数据结构

程序设计 = 数据结构 + 算法

因为结构定义了一个程序的架构,结构定下来了才有具体的实现。好比盖房子,数据结构就是房子的框架结构,如果一间房子很大,而你并不清楚这个房子的结构,会在这里面迷路。

而对于算法,如果属于暂时不需要深究的细节部分,可以参考前面“区分主线和支线剧情”部分,先了解其入口、出口参数以及作用即可。

Linus说: “烂程序员关心的是代码。好程序员关心的是数据结构和它们之间的关系。”

因此,在阅读一份代码时,厘清核心的数据结构之间的关系尤其重要。

6、5 添加日志 and 单步调试(情景分析)

看文档或看代码的过程中,对一些技术点,是需要验证的。

所谓的“情景分析”,就是自己构造一些情景,然后通过加断点、调试语句等分析在这些场景下的行为。

我惯用的做法,是在某个重要的入口函数上面加上断点,然后构造触发场景的调试代码,当代码在断点处停下,通过查看堆栈、变量值等等来观察代码的行为。

情景分析的好处在于:不会在一个项目中大海捞针似的查找,而是能够把问题缩小到一个范围内展开来理解。

从而把个人的时间和精力聚焦到一个小的范围内不断的研究,一直研究到自己满意为止。

修改源码加入日志和打印可以帮助你更好的理解源码。

gdb单步调试,一步步跟踪,是了解源代码最好的途径,没有之一。

6、6 画图梳理源代码逻辑

适当画图来帮助你理解源码,在理清主干后,可以将整个流程画成一张流程图或者标准的UML图,帮助记忆和下一步的阅读。

画图虽然很浪费时间,但是对帮助理解架构和流程很有帮助。

6、7 解决遇到的问题

相信经过前面三个阶段,你已经是各种玩,各种瞎折腾,心里还是有很多疑问吧?

这个功能真牛逼,是如何实现的呢?

我的配置文件为什么没有生效呢?

程序为什么被我搞挂了呢?

我添加的打印为什么没有被执行?

打印的值为什么不符合我的预期?

好!很好!带着这些你心里的问题,去源代码中找答案吧

1.先总体后局部,把握好主体逻辑代码,然后再逐层深入下去

一上来就陷入太多的实现细节是大忌!!!

熟悉主体逻辑后,你大概能知道你感兴趣的代码处于整个项目中的什么模块,什么文件,什么类中

是个什么样的处理流程,定位到具体代码,一行行的分析它,这个过程会持续一段时间。

2.带着问题去看代码

一个系统实现了什么功能,为什么要实现这些功能,是基于什么业务场景?

3.思考自己如何实现类似系统(功能)

如果要我来实现一套类似的系统,我会如何来考虑问题,如何来实现这套系统?

然后再看看别人是如何实现的,找到两者之间的差距,并不断缩小之间的差距。

七、写笔记记录想法,促进思考和总结

1.记录解开问题谜团的过程

2.记录下开源代码中好的设计思想,好的代码技巧,以及任何你觉得好的东西

3.画整个程序的流程图,有利于理解程序的整个流程,而不被代码的细节所干扰。

4.坚持记录源代码学习笔记,记笔记能够有助于更深入的思考,以前很多问题只是浅层思考,不够深入

八、抄写项目源代码

以groupcache来举例,它包括lru,singleflight,protobuf,一致性hash这四个技术

步骤一:抄写前,可以大致了解下这些技术,比如lru是什么?lru解决什么问题?简单了解下lru的实现原理(后续深入具体实现)

步骤二:抄写过程中,会发现和之前了解的实现原理有不同的地方,比如有优化,或者有疑惑点,记录下来,继续抄写。

步骤三:抄写后,针对有疑惑的点,找资料,找人交流,必须搞清楚前面记录的不动的地方。

九、仿写项目

如果感觉自己对于项目理解的还不到位,我推荐一个笨方法,抄项目代码,抄着抄着你就懂了。

如果想进一步深刻的学习到源代码的精髓,可以仿写一个相近的程序进行操练。

理解了这个程序并不表明掌握了这个程序,只有在操练一个相近的程序时,才知道你到底理解了多少,掌握了多少

十、教给他人(输出知识)

检验是否掌握一门技术,就是将知识输出,看他人能否听懂

当自己对项目的方方面面都很熟悉后,可以在B站上开直播,将自己所学的知识传授给他人。

输出形式:

1、csdn博客、知乎、头条、微信公众号等平台发文章

2、组内技术讨论或者微信群讨论

3、公司内技术分享会(正式)

3、在B站上直播,将自己辛辛苦苦学到的知识分享出去

//2021.02.09更新//

阅读开源代码,一上来就陷入技术细节是大忌!!!

上图是总结的学习方法论,

核心概念:

总体架构

核心概念和术语

逻辑关系如何

从使用者的角度来看:

1、提供什么功能?

2、解决什么问题?

3、使用场景和限制是什么

4、如何来使用

横向对比:

功能上相似之处和差异

技术实现原理上相似之处和差异

从设计者角度:

1、为什么这么设计?

2、背后的思想和理论

3、如果我来设计,会从哪些方面来考虑?

 附上原创作者的参考链接原创链接以及个人的转载链接

【原文】如何阅读一份源代码?(2020年版) - codedump的网络日志

【转载】如何阅读一份源代码?(2020年版)_小禾5912的博客-CSDN博客

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一些关于使用Python代码实现Transformer的指导。 首先,您需要安装PyTorch和torchtext库。然后,您可以按照以下步骤实现Transformer模型: 1. 定义输入和输出的词汇表,并使用torchtext库加载数据集。 2. 定义Transformer模型的编码器和解码器部分。 3. 实现自注意力机制(self-attention)和前向神经网络(feed-forward network)。 4. 实现残差连接(residual connection)和层归一化(layer normalization)。 5. 定义Transformer模型的训练和评估过程。 下面是一个简单的示例代码,用于实现一个基本的Transformer模型: ```python import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torchtext.datasets import TranslationDataset, Multi30k from torchtext.data import Field, BucketIterator # 定义输入和输出的词汇表 SRC = Field(tokenize='spacy', tokenizer_language='de', init_token='<sos>', eos_token='<eos>', lower=True) TRG = Field(tokenize='spacy', tokenizer_language='en', init_token='<sos>', eos_token='<eos>', lower=True) # 加载数据集 train_data, valid_data, test_data = Multi30k.splits(exts=('.de', '.en'), fields=(SRC, TRG)) SRC.build_vocab(train_data, min_freq=2) TRG.build_vocab(train_data, min_freq=2) # 定义Transformer模型的编码器和解码器部分 class Encoder(nn.Module): def __init__(self, input_dim, hid_dim, n_layers, n_heads, pf_dim, dropout, device): super().__init__() self.device = device self.tok_embedding = nn.Embedding(input_dim, hid_dim) self.pos_embedding = nn.Embedding(1000, hid_dim) self.layers = nn.ModuleList([EncoderLayer(hid_dim, n_heads, pf_dim, dropout, device) for _ in range(n_layers)]) self.dropout = nn.Dropout(dropout) self.scale = torch.sqrt(torch.FloatTensor([hid_dim])).to(device) def forward(self, src, src_mask): # src: [batch_size, src_len] # src_mask: [batch_size, 1, 1, src_len] batch_size = src.shape[0] src_len = src.shape[1] pos = torch.arange(0, src_len).unsqueeze(0).repeat(batch_size, 1).to(self.device) # pos: [batch_size, src_len] src = self.dropout((self.tok_embedding(src) * self.scale) + self.pos_embedding(pos)) for layer in self.layers: src = layer(src, src_mask) return src class EncoderLayer(nn.Module): def __init__(self, hid_dim, n_heads, pf_dim, dropout, device): super().__init__() self.self_attn_layer_norm = nn.LayerNorm(hid_dim) self.ff_layer_norm = nn.LayerNorm(hid_dim) self.self_attention = MultiHeadAttentionLayer(hid_dim, n_heads, dropout, device) self.positionwise_feedforward = PositionwiseFeedforwardLayer(hid_dim, pf_dim, dropout) self.dropout = nn.Dropout(dropout) def forward(self, src, src_mask):

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值