git不同于类似SVNq种版本理pȝQ虽然熟悉常用的操作可以满_部分需求,但ؓ了在遇到ȝ时不至于靠蛮力去试Q了解git的原理还是很有必要?/p>
通过git理的文件版本信息全部存攑֜根目?code>.git下,E微看下Q?/p>
$ ls .git
COMMIT_EDITMSG HEAD branches description index logs packed-refs
FETCH_HEAD ORIG_HEAD config hooks info objects refs
git除了提供l我们^时常用的一些命令之外,q有很多底层命oQ可以用于查看以上部分文件表C的东西?/p>
理解git里的三个区域概念非常重要。git里很多常用的命o都是围绕着q三个区域来做的。它们分别ؓQ?/p>
git中还有三cd用对象(实际不止三种Q,理解q三cd象也很重要。分别ؓQ?/p>
所有对象都会以文g的Ş式保存在.git/objects
目录Q一个对象一个文件?/p>
接下来把上面所有的内容兌h。做以下操作Q?/p>
$ mkdir test && cd test
$ git init
$ ls -a .git/objects # 没有文g
. .. info pack
$ touch readme # working directory里增加了一个readme文g
$ git add readme # d一个文件到stage区域
$ git ls-files --stage # q个命o可以查看stage区域里的内容Q可以看到有readme
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
$ ls -a .git/objects # 同时.git/objects增加了一个e6的目?
. .. e6 info pack
$ ls -a .git/objects/e6/ # e6目录下增加了一个文?
. .. 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
上面的操作展CZgit中三个区域三个对象的部分兌关系。git中每个对象都以一?0个字W长度的SHA-1哈希gؓ标识Q以q?0个字W的?个字W作为文件夹Q以?8个字Wؓ文g名?/p>
Z以上l箋操作Q?/p>
$ git commit -m 'first commit' # commit会将stage里标识的文g提交到history区域
[master (root-commit) 8bf6969] first commit
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 readme
$ ls -a .git/objects # 增加?个文Ӟ也就?个对?
. .. 8b e6 e8 info pack
$ git ls-files --stage # stage仅表C当前被版本理的文Ӟ所以内容不?
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
# git cat-file 命o可以用于查看.git/objects下的文gQ意卛_用于查看对象
$ git cat-file -t e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 # q个是之前git add readme产生的文件对?blob
blob
# 同样我们来查看git commit -m后新增的两个对象
$ ls .git/objects/8b/
f696927c17526eb8f0c6dae8badb968a001ed0
$ git cat-file -t 8bf696927c17526eb8f0c6dae8badb968a001ed0 # 记得带上8bq个文g夹名Q才一个完整的对象ID。这是一个commit对象
commit
$ ls .git/objects/e8
0ad49ace82167de62e498622d70377d913c79e
$ git cat-file -t e80ad49ace82167de62e498622d70377d913c79e # tree对象
tree
区域和对象如何交互的可以用下图描qͼ
通过git cat-file -p
可以查看对象的更多描qͼgit cat-file -t
仅获取对象的cd。做以下操作获得更深的认识:
# q个commit对象记录了提交者的信息Q还包括指向的tree对象
$ git cat-file -p 8bf696927c17526eb8f0c6dae8badb968a001ed0
tree e80ad49ace82167de62e498622d70377d913c79e
author Kevin Lynx <kevinlynx@gmail.com> 1410090424 +0800
committer Kevin Lynx <kevinlynx@gmail.com> 1410090424 +0800
first commit
# 查看tree对象可以看出tree指向的blob对象
$ git cat-file -p e80ad49ace82167de62e498622d70377d913c79e
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 readme
即是已l被版本理的文Ӟ发生改动后(正常改动或合qӞ都?code>git add来重新mark它。创建第二次提交q一步认识:
$ echo 'hello git' > readme
$ touch install
$ git ls-files --stage # 不用git addQ暂存区域内Ҏ?
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
# 此时stage里内Ҏ变,提示no changes added to commit
$ git commit
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: readme
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# install
no changes added to commit (use "git add" and/or "git commit -a")
$ git add readme
$ ls .git/objects/ # git add之后.git/objects下新增文?
8b 8d e6 e8 info pack
$ ls .git/objects/8d/
0e41234f24b6da002d962a26c2495ea16a425f
$ git cat-file -p 8d0e41234f24b6da002d962a26c2495ea16a425f # 查看该新增对?
hello git
# q个时候还可以在提交前撤销git add readme
$ git reset readme # 从history到stage
Unstaged changes after reset:
M readme
$ cat readme
hello git
$ git checkout readme # 从stage到working directory
$ cat readme # 没有内容Q回到第一个版?
$ git add install # d新创建的文g
$ git ls-files --stage # stage中的内容是最新的readme和新d的install
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 install
100644 8d0e41234f24b6da002d962a26c2495ea16a425f 0 readme
$ ls .git/objects/
8b 8d e6 e8 info pack
以上Q发C个有的现象Q新加的install
文g的SHA-1哈希值和之前?code>readme相同Q这是因2个文仉是空的,内容相同。l:
$ git commit -m 'second commit'
$ ls .git/objects/ # 提交后新?个对?
45 72 8b 8d e6 e8 info pack
$ ls .git/objects/72/
b94e949c5fca6092cc74c751a7bb35ee71c283
$ git cat-file -p 72b94e949c5fca6092cc74c751a7bb35ee71c283
tree 45cf0bd049d7eea4558b14f33a894db27c7c1130 # 新创建的tree对象
parent 8bf696927c17526eb8f0c6dae8badb968a001ed0 # commit对象有parentQ正是上一ơ提?
author Kevin Lynx <kevinlynx@gmail.com> 1410094456 +0800
committer Kevin Lynx <kevinlynx@gmail.com> 1410094456 +0800
second commit
# 新创建的tree对象指向?个文?
$ git cat-file -p 45cf0bd049d7eea4558b14f33a894db27c7c1130
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 install
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f readme
需要注意,有时候我们?code>git commit -aQ它会直接将已经加入版本理的文件一h交,从而蟩q了git add
q个q程。同git很多操作一P它只是一个快h作?/p>
从上面的内容其实已经可以看出git的优势所在,它可以完全不需要服务器完成一个版本控制系l的所有事情。在.git文g中它记录了所有的文g的所有历史提交,记录了每一ơ提交的信息?/p>
git的常用操作中q会涉及到分支、远端仓库等Q空了再写?/p>
因ؓ之前一直处在游戏开发行业,׃U种原因一直对软g工程中的目理、项目开发方法缺乏体验。虽焉目中也曾倡导~写更多的文档,无论是模块说明文档还是设计文档,但效果一直不好。不甚理想的地方主要体现在文档的规范性欠~、不l一、Q于表面没有实质内宏V文档的~写~Z详尽的方法指|那么所谓的设计文档要么是用来敷衍上U要么就是随着开发h员的水^不一而千差万别?/p>
当我开始目前这个非游戏目Ӟ我也曾想Q前期做好结构设计,制定好关键问题的解决ҎQ那么要完成q个目׃在话下了。但是我很快面临了一个问题:需求不定。回惌n处游戏公司的那些日子Q程序员L抱怨策划需求变更过快过多,在每一ơ策划提Z个需求变更时Q}慎的E序员都会再三让{划保证Q放心,不会变了。而我面的问题则更ؓ严峻。我意识刎ͼ目的需求,p用户也无法一一|列出来。我们需要的是需求调研。但q你将客户的所有需求全部挖掘出来后Q这几乎不可能,因ؓ他们自己也不太清楚自己想要什么)Q当你交付了W一个Y件版本,几乎可以肯定客户会提Z大堆的需求变_我要的不是这个,我要的那个怎么没有Q哦Q我当初以ؓ你说的是另一个意思?/p>
当然Q需求调研这U工作不是让E序员去做的Q那会更悲剧Q无论是对客戯是对E序员而言Q他们都是在对牛弹琴Q。但需求的不确定性也L存在的?/p>
事实上,需求变化本w就是一个很正常的现象。我一向愿意更悲观地处理Y件开发方面的问题Q所谓小心得万q船。基于此Q我军_摆好心态学学Y件开发的Ҏ学?/p>
本文要描q、ȝ了RUP开发方法学的主要内容,l合我自q感受阐述了一些RUP的核心原则。我怿我所理解的内Ҏ肤浅的,对于非代码的表达我更怿其是存在歧义的。所以本文仅当是一U经验参考,不必当真?/p>
RUP据传是用于指导大型甚臌大型目开发的Q我们做的不是这栯模的目。但是我们需要记录下整个目的开发过E,通过q个q程中出的工gM一个h可以看出q个目是如何实现出来的Q其目的在于规避唯有从v量代码中才能熟悉目实现q种问题。这里出C一个概念:工gQ其指的是Y仉目开发过E中M留下记录的事物,例如文档、图、代码等?strong>RUP的一个重要思想Q在于其整个软g开发过E都是可推导?/strong>。例如我们通常说的软g架构Q或一点的模块l构Q都是通过开发过E中前面阶段产出的工件推导得出,?strong>不是凭借程序员的经验拍脑袋惛_来的Q经验不太可靠,q且千差万别Q?strong>推导意味着每个环节变得可靠)。我们借助RUP的这个特性,创徏q些工gQ用以徏立v软g实现的可靠知识库?/p>
以下均摘?lt;Thinking in UML>中对RUP的描qͼ
l一q程归纳和集成了软g开发活动中的最佛_践,它定义了软g开发过E中最重要的阶D和工作Q四个阶D和九个核心工作)Q定义了参与软g开发过E的各种角色和他们的职责Q还定义了Y件生产过E中产生的工Ӟq提供了模板。最后,采用演进式Y件生命周期(q代Q将工作、角色和工g串在一P形成了统一q程?/p>
l一q程是一U追求稳定的软gҎQ它q求开发稳定架构,控制变更
l一q程集成了面向对象方法、UML语言、核心工作流、工件模板和q程指导{知?/p>
单来_RUP作ؓ一UY仉目开发方法学Q?strong>它定义了软g开发的每一个过E,最重要的是它指g在每一个过E需要Z么,q些产出又是怎样得到。它试图规范化整个流E,以规遉K求变_目参与者水q不一{带来的目不可控等问题Q以期一个Y件品稳定地开发出?/strong>。在一个项目开发过E中Q最核心的资源是人,最不可控的亦是人?/p>
我觉得要快速学习一U知识,需要首先获得这门知识的M框架。另一斚wQ在我们获得更多信息后,我们需要挖掘出q门知识的核心思想。学习RUP我觉得从q两斚w入手是相Ҏ较快速和有效的手Dc?/p>
RUP定义了Y件开发过E的四个阶段Q以?个工作流E?/strong>。一张极为经典的RUP开发过E框架图如下Q?/p>
RUP整个Y件开发过E分为四个阶D: 每一个阶D늚工作分ؓ9个流E: 其中Q前6个流E被l称?#8221;engineering disciplines”Q后3个流E被UCؓ”supporting disciplines”。当Ӟ我们主要x?个流E。那么,q些工作程和开发阶D又有什么关pdQ上图中其实已经阐明了这些关pR?/p> RUP指导q代开发。在软g开发的q?个阶D中Q每一个阶D会被分q次q代。而每一ơP代则늛了这9个工作流E。随着开发阶D向产品化靠q,自然而然圎ͼ需求的变更、增加自然会减少Q所以从图中可以看出Q开发过E越到后期,其工作流E中关于需求的工作则越。同P在先启阶D,光求相关的工作则占据了该阶D늚主要工作内容?/p>
RUP中的q代要求在每一ơP代中Q都会完整地实施一遍整个工作流E。在软g实施阶段Q甚至会在每一个P代过E完后输Z个可q行的Y件版本。这个版本可能会被交付给客户Q以期进一步地在功能需求上取得与客户一致的意见。这倒是同敏捷开发有点类伹{?/p> 既然制定了工作流E,那每一个工作流E该如何d施呢Q?strong>RUP制定了每个工作流E需要参与的角色Q这些角色需要从事的zdQ以及这些活动生的工g?/strong> q句话实际上反映了RUP的一个重要信息,摘自wikiQ?/p> RUP is based on a set of building blocks, or content elements, describing what is to be produced, the necessary skills required and the step-by-step explanation describing how specific development goals are to be achieved. The main building blocks, or content elements, are the following: 在我看来QRUP每个工作程所完成的工作,是一个徏模的q程。所谓徏模,单来说就是将需要描q的事物通过更系l的形式表达出来Q以期获得对该事物更深入的理解?lt;Thinking in UML>中定义徏模概念ؓQ?/p>
建模(modeling)Q是指通过对客观事物徏立一U抽象的Ҏ用以表征事物q获得对事物本n的理解,同时把这U理解概念化Q将q些逻辑概念l织hQ构成一U对所观察的对象的内部l构和工作原理的便于理解的表达?/p> 在这里,建模的过E需要用一些工兗在RUP中徏模用UML来完成。在<Thinking in UML>中讲qCUML的核心模型,包括Q?/p> 可能在大家的普遍认识中,UML无非是几种图,q且_看一眼理解v来也不困难,甚至q能用来ȝcd做下代码l构设计。但UML的作用不仅仅如此?/p> 以上所描述的UML核心模型中,每个模型q不单指的的是一UUML图。每个模型实际上都会包含几种UML图,会包含若q张UML图。这些模型基本上渗透于RUP?个工作流E中Q只不过不同的工作流E用的模型比重不一而已?/p>
例如?#8220;分析设计”工作程中,可能会用到pȝ用例模型、分析模型、Y件架构和框架、设计模型等Q而业务用例模型可能在q个程中根本不会用刎ͼ相反Q业务用例模型则可能?#8220;业务建模”程中被q泛使用?/p> 前已q及在RUP的每个工作流E中QRUP定义了该程需要参与的角色Q以及这些角色需要进行的zdQ例如这里可以看?#8220;分析设计”程中的角色和活动集Q摘?lt;Thinking in UML>Q: 相应的,在该工作程中需要出的工g集ؓQ摘?lt;Thinking in UML>Q: 既然使用了UML作ؓ建模工具Q所以可以简单地说这些工件主要就是UML图,当然也会有其他文档性质的事物,例如|络协议l构、数据库表等UML无法描述的东西则通过普通文字性文档描q?/p>
到目前ؓ止我们已l了解到RUP定义了开发过E?phase)Q定义了每个q程包含的若q工作流E,q定义了每个工作程需要哪些角色从事哪些活动来完成哪些工g。除此之外,RUPq提供了6条最佛_는以指DY件开发: q些实践在我看来仅仅是一些项目开发的指导原则Q它们渗透到每一个过E,每一个工作流E。在目q程中实践这些原则,用以保目的成功。例如我们用UML建模Q以辑ֈ“可视建模”。我们通过建立需求用例,?#8220;理需?#8221;?/p> g没有文档来专门阐qRUP的核心思想Q但我觉得掌握其核心思想才是学习的要Ҏ在。要理解一UY件开发方法学的核心思想Q其实最好是其与别的方法学做比较。这里先我的一些感惛_阐述?/p> 用例驱动指的是整个Y仉目的推进q程Q是依靠“用例”来完成?lt;Thinking in UML>Q?/p> 在实际的软g目中,一个Y件要实现的功能通过用例来捕P接下来的所有分析、设计、实现、测试都q例来取得Q即以实现用例ؓ目标。在l一q程中用例能够驱动的不仅仅是分析设计?/p> 用例单来说就是描qC一个系l功能。但必须注意的是Q这仅仅是它定义的一部分。用例主要分布在“业务建模”?#8220;需?#8221;?#8220;分析设计”q些工作q程中。在不同的过E中用例的粒度和性质都不一栗例如对于一个借书pȝ而言Q在业务建模阶段Q我们可以获取出一?#8220;借书”用例Q其pȝ边界甚至不是pȝ而可能仅xq个业务本nQ因个阶D还没有考虑到计机如何实现q个借书业务Q;在系l分析阶D,我们可以将“借书”q个用例l化为用户和计算Y件系l的交互Q进一步地Q我们可能会q一步精化这个用例,例如用户通过|页l端“借书”。(q里描述了很多徏模的l节Q可不必qQ本文只l出一个概要性的介绍Q?/p> 我们说用例驱动Y件开发,但它如何驱动的呢Q我在实际的建模q程中,最明显的感受就是用例驱动了整个建模q程?/p> q入设计阶段后,虽然可以q一步徏模,得到会直接映到代码的类图、序列图{,但这L囑֜面需求变更时Q基本上会面临修改,q意味着l护q些文档需要耗费_֊。所以,<Thinking in UML>中主张将_֊攑֜l护分析cL型中Q而通过其他U定实现从分析类到实际代码的转换。我觉得q个也在理?/p> 我个得RUP的一大特点在于规范化了整个Y件开发过E,每一个步骤需要哪些角色参与,该干什么,怎么d都有指导。加之这些活动的”可推导?#8220;Q这意味着不论参与角色属于什么水qI都可以稳固地推进目q程?/p>
此外Q这U规范化也会l项目留下详l的演化q程。你可以明确地看到整个Y件是如何演化出最l的产品代码Q可以深入地理解目代码中的设计?/p> 我只是一个RUP新手Q即便如此,我依然不觉得RUP是Y件开发的万能药。我怿M软g开发方法都是有局限性的。我们在实际使用的时候也只是吸取其精华。不同的开发方法其适用范围也是不一L。正如有人将RUP和XP做比较时_如果你用RUPd一个杂货铺Q在没开张之前你已l破产了Q同样如果你用XPd飞机Q飞机毁了十来次也许才能做出来(from <Thinking in UML> againQ?/p>
RUP建模
RUP最佛_?/h3>
RUP核心思想
用例驱动
规范化整个过E?/h3>
ȝ
参考文?/h2>
参?a >Why is processing a sorted array faster than an unsorted array?
看以下代码:
#include <algorithm>
#include <ctime>
#include <iostream>
int main()
{
// generate data
const unsigned arraySize = 32768;
int data[arraySize];
for (unsigned c = 0; c < arraySize; ++c)
data[c] = std::rand() % 256;
// !!! with this, the next loop runs faster
std::sort(data, data + arraySize);
// test
clock_t start = clock();
long long sum = 0;
for (unsigned i = 0; i < 100000; ++i)
{
// primary loop
for (unsigned c = 0; c < arraySize; ++c)
{
if (data[c] >= 128)
sum += data[c];
}
}
double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
std::cout << elapsedTime << std::endl;
std::cout << "sum = " << sum << std::endl;
}
问题在于,Lstd::sort
那一行,以上代码运行更长的旉。在我的机器上未Lstd::sort
耗时8.99sQ去掉后耗时24.78s。编译器使用的是gcc4.4.3。事实上Q以上代码跟~译器没有关p,甚至跟语a没有关系。那q是Z么呢Q?/p>
q跟处理q个数组的逻辑有非常大的关pR如以上代码所C,q个循环里有个条件判断。条件判断被~译成二q制代码后,是一个蟩转指令,cMQ?/p>
具体Z么会不同Q这涉及到计机CPU执行指o时的行ؓ?/p>
惌现在有一堆指令等待CPUL行,那么CPU是如何执行的呢?具体的细节可以找一本计机l成原理的书来看。CPU执行一堆指令时Qƈ不是单纯C条一条取出来执行Q而是按照一U流水线的方式,在CPU真正执行一条指令前Q这条指令就像工厂里水U生产的产品一P已经被经q一些处理。简单来_一条指令可能经q这些过E:取指(Fetch)、解?Decode)、执?Execute)、放?Write-back)?/p>
假设现在有指令序列ABCDEFG。当CPU正在执行(execute)指oAӞCPU的其他处理单元(CPU是由若干部g构成的)其实已经预先处理C指oA后面的指令,例如B可能已经被解码,C已经被取指。这是水U执行,q可以保证CPU高效地执行指令?/p>
如上所_CPU在执行一堆顺序执行的指oӞ因ؓ对于执行指o的部件来_其基本不需要等待,因ؓ诸如取指、解码这些过E早p做了。但是,当CPU面非顺序执行的指o序列Ӟ例如之前提到的蟩转指令,情况会怎样呢?
取指、解码这些CPU单元q不知道E序程会蟩转,只有当CPU执行到蟩转指令本w时Q才知道该不该蟩转。所以,取指解码q些单元׃l箋取蟩转指令之后的指o。当CPU执行到蟩转指令时Q如果真的发生了跌{Q那么之前的预处理(取指、解码)q做了。这个时候,CPU得从跌{目标处时取指、解码,然后才开始执行,q意味着QCPU停了若干个时钟周期!
q其实是个问题,如果CPU的设计放任这个问题,那么光度很难提升v来。ؓ此,Z发明了一U技术,UCؓbranch predictionQ也是分支预测。分支预的作用Q就是预某个蟩转指令是否会跌{。而CPU根据自q预测到目标地址取指令。这P卛_从一定程度提高运行速度。当Ӟ分支预测在实C有很多方法?/p>
单的预测可以直接使用之前的实际执行结果。例如某个蟩转指令某一ơ生了跌{Q那么下一ơ执行该指oӞCPUq接从跌{目标地址处取指,而不是该跌{指o的下一条指令?/p>
了解了以上信息后Q文章开头提出的问题可以解释了。这个代码中有一个@环,q个循环里有一个条件判断。每一ơCPU执行q个条g判断ӞCPU都可能蟩转到循环开始处的指令,即不执行if后的指o。用分支预技术,当处理已l排序的数组Ӟ在若q次data[c]>=128
都不成立Ӟ或第一ơ不成立Ӟ取决于分支预的实现Q,CPU预测q个分支是始l会跌{到@环开始的指oӞq个时候CPU保持有效的执行Q不需要重新等待到新的地址取指Q同P?code>data[c]>=128条g成立若干ơ后QCPU也可以预这个分支是不必跌{的,那么q个时候CPU也可以保持高效执行?/p>
相反Q如果是无序的数l,CPU的分支预在很大E度上都无法预测成功Q基本就?0%的预成功概率,q将消耗大量的旉Q因为CPU很多旉都会{待取指单元重新取指?/p>
本文完。最后感叹下stackoverflow上这个帖子里那个老外回答问题的专业性,我要是楼L感动得涕泪横飞了。感谢每一个传播知识的人?/p>