介:本文是一個 V8 編譯原理知識的介紹文章,旨在讓大家感性的了解 JavaScript 在 V8 中的解析過程。
作者 | 子弈
來源 | 阿里技術公眾號
本文是一個 V8 編譯原理知識的介紹文章,旨在讓大家感性的了解 JavaScript 在 V8 中的解析過程。本文主要的撰寫流程如下:
本文僅代表個人觀點,文中若有錯誤歡迎指正。
大家可能一直疑惑的問題:JavaScript 是一門解釋型語言嗎?要了解這個問題,首先需要初步了解什么是解釋器和編譯器以及它們的特點是什么。
解釋器的作用是將某種語言編寫的源程序作為輸入,將該源程序執行的結果作為輸出,例如 Perl、Scheme、APL 等都是使用解釋器進行轉換執行:
編譯器的設計是一個非常龐大和復雜的軟件系統設計,在真正設計的時候需要解決兩個相對重要的問題:
中間表示(IR)
中間表示(Intermediate Representation,IR)是程序結構的一種表現方式,它會比抽象語法樹(Abstract Syntax Tree,AST)更加接近匯編語言或者指令集,同時也會保留源程序中的一些高級信息,具體作用包括:
優化編譯器
IR 本身可以做到多趟迭代從而優化源程序,在每一趟迭代的過程中可以研究代碼并記錄優化的細節,方便后續的迭代查找并利用這些優化信息,最終可以高效輸出更優的目標程序:
優化器可以對 IR 進行一趟或者多趟處理,從而生成更快執行速度或者更小體積的目標程序(例如找到循環中不變的計算并對其進行優化從而減少運算次數),也可能用于產生更少異常或者更低功耗的目標程序。除此之外,前端和后端內部還可以細分為多個處理步驟,具體如下圖所示:
解釋器和編譯器的具體特性比較如下所示:
需要注意早期的 Web 前端要求頁面的啟動速度快,因此采用解釋執行的方式,但是頁面在運行的過程中性能相對較低。為了解決這個問題,需要在運行時對 JavaScript 代碼進行優化,因此在 JavaScript 的解析引擎中引入了 JIT 技術。
JIT (Just In Time)編譯器是一種動態編譯技術,相對于傳統編譯器而言,最大的區別在于編譯時和運行時不分離,是一種在運行的過程中對代碼進行動態編譯的技術。
為了解決 JavaScript 在運行時性能較慢的問題,可以通過引入 JIT 技術,并采用混合動態編譯的方式來提升 JavaScript 的運行性能,具體思路如下所示:
采用上述編譯框架后,可以使得 JavaScript 語言:
V8 是一個開源的 JavaScript 虛擬機,目前主要用在 Chrome 瀏覽器(包括開源的 Chromium)以及 Node.js 中,核心功能是用于解析和執行 JavaScript 語言。為了解決早期 JavaScript 運行性能差的問題,V8 經歷了多個歷史的編譯框架衍變之后(感興趣的同學可以了解一下早期的 V8 編譯框架設計),引入混合動態編譯的技術來解決問題,具體詳細的編譯框架如下所示:
Ignition 的主要作用是將 AST 轉換成 Bytecode(字節碼,中間表示)。在運行的過程中,還會使用類型反饋(TypeFeedback)技術并計算熱點代碼(HotSpot,重復被運行的代碼,可以是方法也可以是循環體),最終交給 TurboFan 進行動態運行時的編譯優化。Ignition 的解釋執行流程如下所示:
在字節碼解釋執行的過程中,會將需要進行性能優化的運行時信息指向對應的 Feedback Vector(反饋向量,之前也被稱為 Type Feedback Vector),Feeback Vector 中會包含根據內聯緩存(Inline Cache,IC)來存儲的多種類型的插槽(Feedback Vector Slot)信息,例如 BinaryOp 插槽(二進制操作結果的數據類型)、Invocation Count(函數的調用次數)以及 Optimized Code 信息等。
這里不會過多講解每個執行流程的細節問題。
TurboFan 利用了 JIT 編譯技術,主要作用是對 JavaScript 代碼進行運行時編譯優化,具體的流程如下所示:
圖片出處 An Introduction to Speculative Optimization in V8。
需要注意 Profiling Feedback 部分,這里主要提供 Ignition 解釋執行過程中生成的運行時反饋向量信息 Feedback Vector ,Turbofan 會結合字節碼以及反饋向量信息生成圖示(數據結構中的圖結構),并將圖傳遞給前端部分,之后會根據反饋向量信息對代碼進行優化和去優化。
這里的去優化是指讓代碼回退到 Ignition 進行解釋執行,去優化本質是因為機器碼已經不能滿足運行訴求,例如一個變量從 string 類型轉變成 number 類型,機器碼編譯的是 string 類型,此時已經無法再滿足運行訴求,因此 V8 會執行去優化動作,將代碼回退到 Ignition 進行解釋執行。
在了解 V8 的編譯原理之后,接下來需要使用 V8 的調試工具來具體查看 JavaScript 的編譯和運行信息,從而加深我們對 V8 的編譯過程認知。
如果想了解 JavaScript 在 V8 中的編譯時和運行時信息,可以使用調試工具 D8。D8 是 V8 引擎的命令行 Shell,可以查看 AST 生成、中間代碼 ByteCode、優化代碼、反優化代碼、優化編譯器的統計數據、代碼的 GC 等信息。D8 的安裝方式有很多,如下所示:
本文使用方法三安裝 v8-debug 工具,安裝完成后執行 v8-debug --help 可以查看有哪些命令:
# 執行 help 命令查看支持的參數
v8-debug --help
Synopsis:
shell [options] [--shell] [<file>...]
d8 [options] [-e <string>] [--shell] [[--module|--web-snapshot] <file>...]
-e execute a string in V8
--shell run an interactive JavaScript shell
--module execute a file as a JavaScript module
--web-snapshot execute a file as a web snapshot
SSE3=1 SSSE3=1 SSE4_1=1 SSE4_2=1 SAHF=1 AVX=1 AVX2=1 FMA3=1 BMI1=1 BMI2=1 LZCNT=1 POPCNT=1 ATOM=0
The following syntax for options is accepted (both '-' and '--' are ok):
--flag (bool flags only)
--no-flag (bool flags only)
--flag=value (non-bool flags only, no spaces around '=')
--flag value (non-bool flags only)
-- (captures all remaining args in JavaScript)
Options:
# 打印生成的字節碼
--print-bytecode (print bytecode generated by ignition interpreter)
type: bool default: --noprint-bytecode
# 跟蹤被優化的信息
--trace-opt (trace optimized compilation)
type: bool default: --notrace-opt
--trace-opt-verbose (extra verbose optimized compilation tracing)
type: bool default: --notrace-opt-verbose
--trace-opt-stats (trace optimized compilation statistics)
type: bool default: --notrace-opt-stats
# 跟蹤去優化的信息
--trace-deopt (trace deoptimization)
type: bool default: --notrace-deopt
--log-deopt (log deoptimization)
type: bool default: --nolog-deopt
--trace-deopt-verbose (extra verbose deoptimization tracing)
type: bool default: --notrace-deopt-verbose
--print-deopt-stress (print number of possible deopt points)
# 查看編譯生成的 AST
--print-ast (print source AST)
type: bool default: --noprint-ast
# 查看編譯生成的代碼
--print-code (print generated code)
type: bool default: --noprint-code
# 查看優化后的代碼
--print-opt-code (print optimized code)
type: bool default: --noprint-opt-code
# 允許在源代碼中使用 V8 提供的原生 API 語法
--allow-natives-syntax (allow natives syntax)
type: bool default: --noallow-natives-syntax
我們編寫一個 index.js 文件,在文件中寫入 JavaScript 代碼,執行一個簡單的 add 函數:
function add(x, y) {
return x + y
}
console.log(add(1, 2));
使用 --print-ast 參數可以打印 add 函數的 AST 信息:
v8-debug --print-ast ./index.js
[generating bytecode for function: ]
--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . FUNCTION "add" = function add
. EXPRESSION STATEMENT at 41
. . ASSIGN at -1
. . . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"
. . . CALL
. . . . PROPERTY at 49
. . . . . VAR PROXY unallocated (0x7fb8c080e6f0) (mode = DYNAMIC_GLOBAL, assigned = false) "console"
. . . . . NAME log
. . . . CALL
. . . . . VAR PROXY unallocated (0x7fb8c080e470) (mode = VAR, assigned = true) "add"
. . . . . LITERAL 1
. . . . . LITERAL 2
. RETURN at -1
. . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"
[generating bytecode for function: add]
--- AST ---
FUNC at 12
. KIND 0
. LITERAL ID 1
. SUSPEND COUNT 0
. NAME "add"
. PARAMS
. . VAR (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . VAR (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
. DECLS
. . VARIABLE (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . VARIABLE (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
. RETURN at 25
. . ADD at 34
. . . VAR PROXY parameter[0] (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . . VAR PROXY parameter[1] (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
我們以圖形化的方式來描述生成的 AST 樹:
VAR PROXY 節點在真正的分析階段會連接到對應地址的 VAR 節點。
AST 會經過 Ignition 解釋器的 BytecodeGenerator 函數生成字節碼(中間表示),我們可以通過 --print-bytecode 參數來打印字節碼信息:
v8-debug --print-bytecode ./index.js
[generated bytecode for function: (0x3ab2082933f5 <SharedFunctionInfo>)]
Bytecode length: 43
Parameter count 1
Register count 6
Frame size 48
OSR nesting level: 0
Bytecode Age: 0
0x3ab2082934be @ 0 : 13 00 LdaConstant [0]
0x3ab2082934c0 @ 2 : c3 Star1
0x3ab2082934c1 @ 3 : 19 fe f8 Mov <closure>, r2
0x3ab2082934c4 @ 6 : 65 52 01 f9 02 CallRuntime [DeclareGlobals], r1-r2
0x3ab2082934c9 @ 11 : 21 01 00 LdaGlobal [1], [0]
0x3ab2082934cc @ 14 : c2 Star2
0x3ab2082934cd @ 15 : 2d f8 02 02 LdaNamedProperty r2, [2], [2]
0x3ab2082934d1 @ 19 : c3 Star1
0x3ab2082934d2 @ 20 : 21 03 04 LdaGlobal [3], [4]
0x3ab2082934d5 @ 23 : c1 Star3
0x3ab2082934d6 @ 24 : 0d 01 LdaSmi [1]
0x3ab2082934d8 @ 26 : c0 Star4
0x3ab2082934d9 @ 27 : 0d 02 LdaSmi [2]
0x3ab2082934db @ 29 : bf Star5
0x3ab2082934dc @ 30 : 63 f7 f6 f5 06 CallUndefinedReceiver2 r3, r4, r5, [6]
0x3ab2082934e1 @ 35 : c1 Star3
0x3ab2082934e2 @ 36 : 5e f9 f8 f7 08 CallProperty1 r1, r2, r3, [8]
0x3ab2082934e7 @ 41 : c4 Star0
0x3ab2082934e8 @ 42 : a9 Return
Constant pool (size = 4)
0x3ab208293485: [FixedArray] in OldSpace
- map: 0x3ab208002205 <Map>
- length: 4
0: 0x3ab20829343d <FixedArray[2]>
1: 0x3ab208202741 <String[7]: #console>
2: 0x3ab20820278d <String[3]: #log>
3: 0x3ab208003f09 <String[3]: #add>
Handler Table (size = 0)
Source Position Table (size = 0)
[generated bytecode for function: add (0x3ab20829344d <SharedFunctionInfo add>)]
Bytecode length: 6
// 接受 3 個參數, 1 個隱式的 this,以及顯式的 x 和 y
Parameter count 3
Register count 0
// 不需要局部變量,因此幀大小為 0
Frame size 0
OSR nesting level: 0
Bytecode Age: 0
0x3ab2082935f6 @ 0 : 0b 04 Ldar a1
0x3ab2082935f8 @ 2 : 39 03 00 Add a0, [0]
0x3ab2082935fb @ 5 : a9 Return
Constant pool (size = 0)
Handler Table (size = 0)
Source Position Table (size = 0)
add 函數主要包含以下 3 個字節碼序列:
// Load Accumulator Register
// 加載寄存器 a1 的值到累加器中
Ldar a1
// 讀取寄存器 a0 的值并累加到累加器中,相加之后的結果會繼續放在累加器中
// [0] 指向 Feedback Vector Slot,Ignition 會收集值的分析信息,為后續的 TurboFan 優化做準備
Add a0, [0]
// 轉交控制權給調用者,并返回累加器中的值
Return
這里 Ignition 的解釋執行這些字節碼采用的是一地址指令結構的寄存器架構。
關于更多字節碼的信息可查看 Understanding V8’s Bytecode。
JavaScript 是弱類型語言,不會像強類型語言那樣需要限定函數調用的形參數據類型,而是可以非常靈活的傳入各種類型的參數進行處理,如下所示:
function add(x, y) {
// + 操作符是 JavaScript 中非常復雜的一個操作
return x + y
}
add(1, 2);
add('1', 2);
add(, 2);
add(undefined, 2);
add([], 2);
add({}, 2);
add([], {});
為了可以進行 + 操作符運算,在底層執行的時候往往需要調用很多 API,比如 ToPrimitive(判斷是否是對象)、ToString、ToNumber 等,將傳入的參數進行符合 + 操作符的數據轉換處理。
在這里 V8 會對 JavaScript 像強類型語言那樣對形參 x 和 y 進行推測,這樣就可以在運行的過程中排除一些副作用分支代碼,同時這里也會預測代碼不會拋出異常,因此可以對代碼進行優化,從而達到最高的運行性能。在 Ignition 中通過字節碼來收集反饋信息(Feedback Vector),如下所示:
為了查看 add 函數的運行時反饋信息,我們可以通過 V8 提供的 Native API 來打印 add 函數的運行時信息,具體如下所示:
function add(x, y) {
return x + y
}
// 注意這里默認采用了 ClosureFeedbackCellArray,為了查看效果,強制開啟 FeedbackVector
// 更多信息查看: A lighter V8:https://v8.dev/blog/v8-lite
%EnsureFeedbackVectorForFunction(add);
add(1, 2);
// 打印 add 詳細的運行時信息
%DebugPrint(add);
通過 --allow-natives-syntax 參數可以在 JavaScript 中調用 %DebugPrint 底層 Native API(更多 API 可以查看 V8 的 runtime.h 頭文件):
點擊鏈接查看原文V8 編譯淺談,關注公眾號【阿里技術】獲取更多福利!
版權聲明:本文內容由阿里云實名注冊用戶自發貢獻,版權歸原作者所有,阿里云開發者社區不擁有其著作權,亦不承擔相應法律責任。具體規則請查看《阿里云開發者社區用戶服務協議》和《阿里云開發者社區知識產權保護指引》。如果您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將立刻刪除涉嫌侵權內容。
TML 實例
<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>菜鳥教程(runoob.com)</title></head><body><h1>我的第一個標題</h1><p>我的第一個段落。</p></body></html>
實例解析
DOCTYPE 聲明了文檔類型
位于標簽 <html> 與 </html> 描述了文檔類型
位于標簽 <body> 與 </body> 為可視化網頁內容
位于標簽 <h1> 與 </h1> 作為一個標題使用
位于標簽 <p> 與 </p> 作為一個段落顯示
<!DOCTYPE html> 在HTML5中也是描述了文檔類型。 |
什么是HTML?
HTML 是用來描述網頁的一種語言。
HTML 指的是超文本標記語言: HyperText Markup Language
HTML 不是一種編程語言,而是一種標記語言
標記語言是一套標記標簽 (markup tag)
HTML 使用標記標簽來描述網頁
HTML 文檔包含了HTML 標簽及文本內容
HTML文檔也叫做 web 頁面
HTML 標簽
HTML 標記標簽通常被稱為 HTML 標簽 (HTML tag)。
HTML 標簽是由尖括號包圍的關鍵詞,比如 <html>
HTML 標簽通常是成對出現的,比如 <b> 和 </b>
標簽對中的第一個標簽是開始標簽,第二個標簽是結束標簽
開始和結束標簽也被稱為開放標簽和閉合標簽
<標簽>內容</標簽>
HTML 元素
"HTML 標簽" 和 "HTML 元素" 通常都是描述同樣的意思.
但是嚴格來講, 一個 HTML 元素包含了開始標簽與結束標簽,如下實例:
HTML 元素:
<p>這是一個段落。</p>
Web 瀏覽器
Web瀏覽器(如谷歌瀏覽器,Internet Explorer,Firefox,Safari)是用于讀取HTML文件,并將其作為網頁顯示。
瀏覽器并不是直接顯示的HTML標簽,但可以使用標簽來決定如何展現HTML頁面的內容給用戶:
HTML 網頁結構
下面是一個可視化的HTML頁面結構:
<html>
<head>
<title>頁面標題</title>
</head>
<body>
<h1>這是一個標題</h1>
<p>這是一個段落。</p>
<p>這是另外一個段落。</p>
</body>
</html>
只有 <body> 區域 (白色部分) 才會在瀏覽器中顯示。 |
HTML版本
從初期的網絡誕生后,已經出現了許多HTML版本:
版本 | 發布時間 |
---|---|
HTML | 1991 |
HTML+ | 1993 |
HTML 2.0 | 1995 |
HTML 3.2 | 1997 |
HTML 4.01 | 1999 |
XHTML 1.0 | 2000 |
HTML5 | 2012 |
XHTML5 | 2013 |
<!DOCTYPE> 聲明
<!DOCTYPE>聲明有助于瀏覽器中正確顯示網頁。
網絡上有很多不同的文件,如果能夠正確聲明HTML的版本,瀏覽器就能正確顯示網頁內容。
doctype 聲明是不區分大小寫的,以下方式均可:
<!DOCTYPE html>
<!DOCTYPE HTML>
<!doctype html>
<!Doctype Html>
通用聲明
HTML5
<!DOCTYPE html>
HTML 4.01
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
XHTML 1.0
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
查看完整網頁聲明類型 DOCTYPE 參考手冊。
中文編碼
目前在大部分瀏覽器中,直接輸出中文會出現中文亂碼的情況,這時候我們就需要在頭部將字符聲明為 UTF-8。
HTML 實例
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>頁面標題</title></head><body><h1>我的第一個標題</h1><p>我的第一個段落。</p></body></html>
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
者:[美] 里德 · 霍夫曼,微軟的董事會成員,曾任PayPal的創始董事
出版社:中信出版社
出版時間:2019年9月
在閃電式擴張的三種核心方法中,第一種也是最基礎的方法是設計能實現指數增長的創新商業模式。
互聯網時代的創業故事就是這種創新商業模式的故事。不妨回想一下.com時代,其時間范圍大致是從1995年網景公司首次公開募股到2000年納斯達克開始崩盤。在此期間,大量初創企業和幾乎每家老牌公司都試圖做好互聯網業務,但幾乎所有公司都失敗了。原因在于,大多數公司只是試圖將現有商業模式剪切并粘貼到新的網絡媒體上。
你不能將一個物種的心臟移植到另一個物種身體里,并期望它茁壯成長。
如果你在1995年問股票市場分析師哪些公司最有可能主宰互聯網,多數人都會指向微軟和時代華納這種已經占據市場的巨頭。還有些人會提到像eToys這樣的“單一業務”網絡初創企業,它結合了久經考驗的商業模式和新穎的在線媒體。
然而,當網絡泡沫破滅后的一地狼藉消失時,仍然生氣勃勃的最成功的公司是圍繞全新商業模式設計的少數初創企業,比如亞馬遜、eBay和谷歌。像eToys這種公司試圖在各種市場上模仿亞馬遜,但沒有亞馬遜的前臺和后臺創新,一旦金融市場開始要求利潤而不僅僅是昂貴的收入增長,它們就會灰飛煙滅。
從那時起,同樣的故事在多次浪潮中不斷重演。當然,這些成功案例都是科技公司。但正如我們所見,僅靠技術創新是不夠的,即使它對未來的影響巨大。像Craigslist(大型網上免費分類廣告網站)、維基百科和IMDb(互聯網電影數據庫)這樣的服務是有影響力的早期互聯網創新者,仍然從未靠自身實現大量(財務)價值。
當創新技術通過創新商業模式推出創新產品和服務時,將真正創造價值。盡管谷歌、阿里巴巴和臉書的商業模式看似顯而易見,甚至是必然的,但從事后來看,它們在推出時并未受到廣泛贊賞。要真正理解為何這些商業模式能成功,我們首先需要明確定義“商業模式”。
正如安德烈亞·奧萬斯在發表于2015年1月《哈佛商業評論》上的文章《什么是商業模式?》中恰如其分地指出的,它足以讓你絞盡腦汁!就本書而言,我們將重點關注基本定義:一家公司的商業模式描述了它如何通過生產、銷售和支持產品來創造財務收益。
亞馬遜、谷歌和臉書等公司與普通公司,甚至其他成功高科技公司的區別在于,它們始終能設計和執行使自身快速實現龐大規模和可持續競爭優勢的商業模式。當然,沒有一種適用于所有公司的完美商業模式,而且試圖找到這樣一種商業模式也是浪費時間。
但多數優秀的商業模式都有某些共同特征。如果你想找到最適合你公司的商業模式,那么在設計時應該盡量放大四個關鍵增長因素,并盡量縮小兩個關鍵增長限制因素。
商業模式需要考慮的最基本的增長因素是市場規模。這種對市場規模的關注可能是顯而易見的,對于初創企業來說,它是融資演講稿最基本的內容,但如果你想創建一家規模龐大的公司,就需要從基礎開始,摒棄服務于過小市場的想法。
大型市場既有大量潛在客戶,也有不同有效渠道來接觸這些客戶。后面這一點很重要,由“世界上每個人”組成的市場可能看起來很大,卻無法以任何有效方式被接觸到。我們在考察推廣這個關鍵增長因素時,將深入討論這個問題。
判斷市場規模,或者什么是融資演講稿和風險資本家常說的TAM(潛在市場規模)并不容易。預測潛在市場規模及其將來的增長前景,是閃電式擴張的主要不確定因素之一。但正如我們將在愛彼迎和優步的案例中看到的,當其他人仍畏葸不前時,正確預測市場規模并據此進行投資,也是獲得意外高收益的主要機會之一。
理想情況下,市場本身也會快速增長,這使小型市場具有吸引力,并使大型市場的吸引力難以抗拒。
在硅谷,對風險投資的競爭給企業家施加了巨大壓力,使他們將重點放在追求大型市場的想法上。風險投資公司可能向投資者—養老基金和大學捐贈基金等有限合伙人—籌集數億甚至數十億美元,這些投資者尋求高于市場的收益率,以補償它們從私營公司身上碰運氣,而不只是投資于可口可樂這類全球性大公司的風險。
為了實現這些高于市場的收益率,風險投資基金至少需要將投資者的錢翻3倍。1億美元的風險投資基金需要在典型的基金生命周期(7~10年)內獲得3億美元收益,以實現高于市場的內部收益率(15%~22%)。一筆10億美元的基金需要30億美元收益。由于多數風險投資要么虧本,要么勉強維持收支平衡,風險資本家實現這些積極目標的唯一現實方式就是依靠少量非常成功的投資。
多數風險資本家都會根據市場規模過濾投資機會。如果一家公司無法實現“風投規模”(通常是年銷售額至少為10億美元的市場),那么大多數風險投資公司都不會投資,即使對方是一家好公司,原因只是它的規模還不足以幫助風險投資公司實現收益3倍于投資金額以上的目標。
評估市場規模時,除了從現有競爭者手中搶占市場份額之外,還要考慮如何通過降低成本和改進產品吸引新客戶來擴大市場,這一點至關重要。
2014年,紐約大學斯特恩商學院的金融學教授阿斯沃斯·達莫達蘭估計優步可能價值約60億美元,根據是其最終贏得價值1 000億美元的全球出租車市場的10%,也就是價值100億美元的市場的能力。根據優步自己的預測,2016年,該公司處理了超過260億美元的支付金額。可以肯定地說,100億美元的市場是被嚴重低估的數字,因為優步及其競爭對手的易用性和低成本擴大了運輸服務市場。
正如在線文件存儲公司Box的創始人阿龍·利維在2014年的一條推文中指出的:“根據現有運營者的市場確定顛覆性創新者的市場規模,就好比根據1910年的馬匹數量確定汽車行業的市場規模。”
導致低估市場規模的另一個因素是忽略了擴張到其他市場的可能。亞馬遜最初叫亞馬遜圖書,定位為“地球上最大的書店”。但杰夫·貝佐斯一直打算將圖書銷售作為亞馬遜向外擴張的橋頭堡,以實現他的“百貨商店”巨大愿景。如今,亞馬遜主導了圖書銷售行業,但由于市場擴張強勁,圖書銷售額還不到亞馬遜總銷售額的7%。
在蘋果公司的財務業績中可以看到相同情況。2017年第一季度,蘋果通過出售個人計算機創造了72億美元收入,這是該公司開創并一度占據主導地位的領域。這個數字當然很漂亮,但是,在同一個財政季度,蘋果的總收入高達784億美元,這意味著蘋果的原始市場只占其總銷售額的不到10%。
我在格雷洛克的同事杰麗·陳曾幫助黛安娜·格林將VMware的虛擬化軟件擴展為一項龐大的業務,杰麗指出:“每家價值10億美元的企業都是從價值1000萬美元的企業開始的。”
但無論是開辟新市場,擴大現有市場,還是依靠相鄰市場獲得投資者想要的“10億美元級企業”,你都需要有一條通往目標的合理路徑。這自然而然地引出了我最愛與企業家討論的增長因素之一:推廣。
強大、可擴張的業務所需的第二個增長因素是推廣。在硅谷,許多人都喜歡專注于打造用史蒂夫·喬布斯的名言來說“絕妙至極”的產品。打造出偉大產品肯定是好事—后面我們將討論產品質量低劣這個增長限制因素,但是,一個殘酷的事實是,推廣出色的優秀產品幾乎總能擊敗推廣不佳的偉大產品。
Dropbox是一家擁有偉大產品的公司,但它的成功是因為其出色的推廣。在里德的“規模化大師”播客采訪中,該公司的創始人兼首席執行官德魯·休斯敦說,他認為太多初創企業忽視了推廣的重要性:
硅谷的大部分正統觀念都是推出優秀產品。我認為這是因為硅谷的大多數公司都無法生存到推出產品的階段之后。你必須擅長推出產品,然后你必須善于吸引用戶,再然后你必須善于建立商業模式。如果你遺漏了這根鏈條中的任何環節,整個鏈條就會瓦解。
在“移動優先”時代,推廣挑戰變得更加嚴峻。在網絡時代,搜索引擎優化和電子郵件鏈接廣泛適用,而且是成功的推廣渠道,但與之不同,移動應用程序商店幾乎不提供偶然發現產品的機會。你在訪問蘋果或谷歌的應用程序商店時,是搜索某個特定產品,很少有人安裝應用程序只是裝著玩。
因此,成功的商業模式創新者(例如Instagram、WhatsApp、Snap)必須找到大范圍推廣其產品而無須花費很多錢的創造性方法。這些推廣方法分為兩大類:利用現有網絡和病毒式傳播。
1.利用現有網絡
新公司很少有投資于廣告宣傳所需的渠道或資源,因此,它們必須找到創造性方法來利用現有網絡推廣產品。
當我在PayPal時,我們推廣支付服務的主要工具之一是結算eBay上的交易。當時,eBay已經是電子商務領域最大的競爭者之一,并且到2000年年初已有1 000萬注冊用戶。我們通過開發軟件來利用這個用戶群,使eBay賣家可以極其輕松地在其所有eBay商品中自動添加“用PayPal支付”按鈕。令人驚訝的是,即使eBay自身有與之競爭的支付服務Billpoint,客戶仍然會這樣做!賣家需要手動將Billpoint添加到他們的每件商品中,而PayPal為他們代勞了。
多年以后,愛彼迎利用在線分類服務Craigslist實現了類似壯舉。根據YC的邁克爾·塞貝爾的建議,愛彼迎建立了一個系統,允許并鼓勵房東將他們的房源交叉發布到規模大得多的Craigslist上。房東被告知,“將你的房源從愛彼迎轉發到Craigslist,會使你的月均收益增加500美元”,并且只需點擊一個按鈕即可實現。這需要嚴謹的技術技巧—與許多平臺不同,Craigslist沒有允許其他軟件與之交互的應用程序編程接口(API),但它是用于推廣創新而非產品創新的技術創新。“這是一種新穎的方法,”愛彼迎的創始人柏思齊談到了整合,“沒有其他網站能實現如此天衣無縫的整合。我們做得非常成功。”
當然,利用現有網絡也會有缺點。現有網絡提供的好處,現有網絡也可以將其拿走。Zynga是一家領先的社交游戲公司,它利用臉書進行推廣取得了巨大成功,但在臉書決定停止允許Zynga游戲玩家向臉書好友發布進度后,Zynga不得不大幅重新設計其推廣模式。Zynga的創始人馬克·平卡斯富有遠見地建立了足夠強大的壟斷地位,這才能應變求存。
相比之下,在谷歌調整其算法,對其所稱的“垃圾”網站內容降權之后,利用谷歌的搜索平臺創造網站流量和廣告收入的所謂內容農場(如Demand Media)從未恢復元氣。
盡管存在這些危險,但利用現有網絡可能是商業模式的關鍵部分,尤其是當這些網絡可以提供“助推火箭”時,日后可以用病毒式傳播或網絡效應作為補充。
2.病毒式傳播
當某個產品的用戶帶來新用戶,而這些新用戶又帶來更多用戶時,就會發生“病毒式”推廣,這非常像傳染性病毒從一個宿主傳播到另一宿主的過程。病毒式傳播可能是自然產生的—產生于產品的正常使用過程中,也可以通過某種獎勵來激勵。
推出領英后,我和團隊投入了大量時間和精力來研究如何改善自然產生的病毒式傳播;也就是說,如何讓現有用戶更方便地邀請好友使用這項服務。我們采用的一種方法是改進傳播工具,它們中的一部分后來成了病毒式傳播的標準工具,比如地址簿導入器。舉例來說,我們開發了允許領英調取用戶的Outlook聯系人的軟件,這使他們能非常輕松地邀請最重要的聯系人。
但同樣重要的是一種意想不到的病毒式傳播來源。事實證明,用戶希望將其領英主頁作為互聯網上的主要職業身份。這樣一種將用戶的全部詳細職業履歷集于一處的介紹頁面,不僅為用戶創造了價值,也為瀏覽頁面的人創造了價值,同時讓瀏覽者意識到他們應該擁有自己的領英簡歷。因此,我們增加了公開簡歷作為一個系統工具,以提升用戶的價值定位和我們的病毒式增長率。
在PayPal,我們將自然產生的病毒式傳播和激勵產生的病毒式傳播結合起來。支付產品本質上適合病毒式傳播。如果有人使用PayPal通過電子郵件向你匯款,你必須設置一個賬戶才能收到款項。但我們通過貨幣激勵措施增強了這種自然產生的病毒式傳播。
如果你推薦一個朋友使用PayPal,你將得到10美元,你的朋友也將得到10美元。這種自然產生的病毒式傳播和激勵產生的病毒式傳播的組合使PayPal每天增長7%~10%。隨著PayPal網絡的增長,我們將激勵標準均降低到5美元,直至最終徹底取消了貨幣激勵。
激勵措施不一定是用貨幣,和PayPal一樣,Dropbox使用了類似方法,結合自然產生的病毒式傳播(用戶與非用戶共享文件時)和激勵產生的病毒式傳播(基本賬戶持有者每成功推薦一個用戶,將獲得500 MB的額外存儲空間;高級賬戶持有者每成功推薦一個用戶,將獲得1 GB的額外存儲空間)來實現增長。
盡管Dropbox通過投資與戴爾等著名計算機制造商建立了合作關系,但德魯·休斯敦將推動該公司快速增長歸功于病毒式傳播,它幫助其用戶數量在短短10天內從推出時的10萬增長到20萬,然后在短短7個月后飆升至100萬。
如果你的推廣策略側重于病毒式傳播,那么你還必須關注用戶留存率。如果新用戶剛進門就轉身離開,那么吸引新用戶進門并不能幫助你實現增長。根據德魯的說法,Dropbox沮喪地發現,激活率顯示只有40%的注冊用戶真正將文件存入Dropbox并將其鏈接到計算機。在接受我的“規模化大師”播客采訪時,德魯描述了一個讓人回想起電視劇《硅谷》(Silicon Valley)的場景(但結局更幸福):
我們所做的是繼續投資Craigslist,并送給任何在網站上停留時間達到半小時的訪客40美元—一個窮人的可用性測試。我們就像在說:“好,請坐。你的電子郵件中有一封Dropbox的邀請函,請用這個電子郵件地址共享一個文件。”我們測試的5個人中沒人成功,甚至沒人接近成功。這真是令人震驚。我們想:“噢,天哪,這是有史以來最糟糕的產品。”于是我們在這張Excel電子表格中列出了大概80處不足,然后逐一打磨這些體驗中的粗疏之處,這才看到我們的激活率開始攀升。
病毒式傳播幾乎總是需要免費或免費增值的產品(即,在某個節點前免費,然后用戶必須付費升級,例如,Dropbox提供2GB的免費存儲空間)。我們不記得有哪家公司利用付費產品的病毒式傳播實現了大規模擴張。
最強大的推廣創新之一是將兩種策略結合起來。臉書通過利用社交網絡自然產生的病毒式傳播(用戶邀請其他用戶加入進來)和以校園為中心的現有網絡(向大學逐家推廣產品)來實現這一目標。當考慮網絡效應時,我們將深入討論臉書的推廣策略。
企業家經常忽視的一個關鍵增長因素是高毛利率的力量。毛利潤是銷售額減去銷售成本,它可能是長期單位經濟的最佳指標。毛利率越高,每一美元銷售額對公司來說就越有價值,因為這意味著每有一美元銷售額,公司就有更多現金可用于增長和擴張。
許多高科技企業默認具有較高毛利率,這就是為什么這個因素經常被忽視。軟件業務的毛利率很高,因為復制軟件的成本基本為零。軟件即服務(SaaS)業務的銷貨成本略高,因為它需要提供某種服務,但是由于有亞馬遜這樣的云提供商,這種成本一直在變小。
相比之下,“傳統經濟”業務的毛利率往往較低。和在商店銷售商品或在餐館提供餐點一樣,種植小麥是一項低利潤率業務。亞馬遜的成功中最令人驚奇的一點是,它能建立基于零售業的大規模業務,而這通常是低利潤率行業。即使是現在,亞馬遜也非常依賴其高利潤的軟件即服務業務—亞馬遜網絡服務(AWS)。2016年,亞馬遜網絡服務占亞馬遜營業收入的150%,這意味著零售業務實際上已經出現虧損。
我們在本書中關注的多數高價值公司的毛利率都超過60%、70%甚至80%。2016年,谷歌的總收入為546億美元,銷售額為897億美元,毛利率為61%;臉書的總收入為239億美元,銷售額為276億美元,毛利率為87%。2015年,領英的毛利率為86%。
正如我們討論過的,亞馬遜屬于異常情況,2016年其總收入為477億美元,銷售額為1 360億美元,毛利率為35%。然而,即使是亞馬遜的毛利率,也高于通用電氣這種“高利潤”傳統公司,后者在2016年的總收入為322億美元,銷售額為1 197億美元,毛利率為27%。
高毛利率是一個強大的增長因素,因為正如下面所闡述的,并非所有收入都天生平等。這里的關鍵觀點是,盡管毛利潤對賣方來說很重要,但它們與買方無關。
你買東西時可曾考慮過毛利率?你會因為皇堡的利潤率低于巨無霸而選擇漢堡王而不選擇麥當勞嗎?通常,你只關注價格以及購買商品帶來的享受。這意味著出售低利潤產品并不一定比出售高利潤產品更容易。因此,如果可能的話,公司應該設計高毛利率的商業模式。
其次,高毛利率業務對投資者具有吸引力,投資者通常會為此類業務產生現金的能力支付溢價。正如著名投資者比爾·格利在其2011年的博客文章中所寫,“并非所有收入都天生平等”,“當所有條件相同時,投資者更喜歡在更高收入下創造出更高邊際利潤的公司。出售同一軟件的更多拷貝(增量成本為零)是一項可以很好地進行規模化的業務”。
當公司為私營時,對投資者有吸引力使公司更容易以更高估值籌集更多資金(稍后我們將深入研究為什么這如此重要),并降低公司上市時的資金成本。這種獲取資本的途徑是能夠為超高速增長融資的關鍵因素。
重要的是注意潛在毛利率與實際毛利率之間的差異。許多閃電式擴張公司,比如亞馬遜或中國硬件制造商華為和小米,其產品定價都有意最大化市場份額而非毛利率。正如杰夫·貝佐斯喜歡說的,“你的利潤就是我的機會”。小米明確將凈利潤率控制在1%~3%,它將這一做法的靈感歸功于Costco(開市客)。在其他所有因素相同的情況下,投資者對潛在毛利率較高的公司估值總是遠遠高于已經最大化實際毛利率的公司。
最后,公司的大部分運營挑戰都是根據收入或單位銷量而非毛利率來衡量的。如果你有100萬個客戶,每年產生1億美元銷售額,那么不管你的毛利率是10%還是80%,為這些客戶提供服務的成本并不會改變,你仍然需要雇用足夠多的員工來響應他們的客服支持請求。但是,當你有8 000萬美元而不是1 000萬美元的毛利潤可用時,更容易提供良好的客服支持。
相反,同樣是達到1 000萬美元毛利潤,向每年產生1 250萬美元銷售額的12.5萬名客戶出售商品并提供服務,與向每年產生1億美元銷售額的100萬名客戶出售商品并提供服務相比,實現前一目標要容易得多。8倍的客戶數量, 8倍的收入,意味著8倍的銷售人員、客戶服務代表、會計師等等。
設計高毛利率的商業模式可以讓你獲得更大的成功機會和更高的成功回報。正如我們將在后面的章節中看到的,高毛利率甚至幫助非科技企業[例如西班牙服裝零售商Zara(颯拉)]成長為全球巨頭。
市場規模、推廣和毛利率都是推動公司增長的重要因素,但最后一個增長因素的關鍵作用在于,它使這種增長能維持足夠長時間,以建立具有極高價值和持久獨家優勢的市場。雖然過去20年前三個增長因素有所改善,但全球互聯網使用率的增長將網絡效應推向了經濟生活中前所未有的水平。
網絡效應變得日益重要,是技術在經濟中的主導地位提高的主要原因之一。
1996年年底,世界上最有價值的5家公司是通用電氣、荷蘭皇家殼牌、可口可樂公司、NTT(日本電報電話公司)以及埃克森美孚,這些都是傳統的工業和消費品公司,依賴龐大的規模經濟和有數十年歷史的品牌推動其價值。僅僅21年后,在2017年第四季度,這個名單就變得截然不同:蘋果、谷歌、微軟、亞馬遜和臉書。這是一個非凡的轉變。
事實上,雖然蘋果和微軟在1996年年底已經是知名公司,但亞馬遜彼時只是一家私營初創企業,拉里·佩奇和謝爾蓋·布林還是斯坦福大學的研究生,距離他們創建谷歌還有兩年時間,而馬克·扎克伯格尚未迎來他的猶太教成年禮。那么,這21年間發生了什么?網絡時代來了,這就是答案。
現在,技術以我們祖輩無法想象的方式將所有人聯系起來。目前,超過20億人有智能手機(其中許多是由蘋果公司制造,或使用谷歌的安卓操作系統),這使他們始終與全球一切網絡息息相關。任何時候,他們都能找到世界上幾乎任何信息(通過谷歌),購買世界上幾乎任何產品(通過亞馬遜或阿里巴巴),或與世界上幾乎任何其他人交流(通過臉書、WhatsApp、Instagram、微信)。
在這個高度互聯的世界中,越來越多的公司能夠利用網絡效應來實現超大規模增長和利潤。
我們將在本書中使用通俗易懂的網絡效應定義:當增加任何一個用戶都會增加產品或服務對于其他用戶的價值時,這種產品或服務就會產生積極的網絡效應。
經濟學家將這種效應稱為“需求方規模經濟”,或者更一般地稱為“正外部性”。
網絡效應的神奇之處在于它會產生正反饋循環,從而導致超線性增長并創造價值。這種超線性效應使網絡中的任何節點都很難從現有選擇切換到替代選擇(“客戶鎖定”),因為任何新競爭者都幾乎不可能與加入現有網絡的價值相提并論。(這些網絡中的節點通常是客戶或用戶,比如傳真機的典型案例或最近的臉書案例,但也可能是數據要素或其他對于企業富有價值的基本資產。)
由此產生的“規模收益遞增”現象通常會導致最終均衡,即單個產品或公司在市場中占主導地位并獲得該行業的大部分利潤。因此,聰明的企業家努力創造(聰明的投資者希望投資)這些網絡效應初創企業也就不足為奇了。
從eBay到臉書再到愛彼迎的幾代初創企業都利用這些驅動因素建立了市場主導地位。要實現這些目標,關鍵是準確理解網絡效應的作用原理。我在格雷洛克的同事西蒙·羅思曼是全球首屈一指的網絡效應專家之一,他為eBay開創了價值140億美元的自動化市場。
西蒙警告說:“很多人都試圖通過添加資料簡介等方式利用網絡效應,他們推論,‘市場上提供了資料簡介這項功能,因此,如果我添加資料簡介,就會產生網絡效應’。”然而,建立網絡效應的實際情況很復雜。最優秀的閃電式擴張公司不是簡單地模仿具體功能,而是研究不同類型的網絡效應并將其設計到公司的商業模式中。
【鈦媒體作者介紹:本文內容節選自《閃電式擴張》作者,[美] 里德 · 霍夫曼。他是著名企業家和投資人,現任微軟的董事會成員,是全球知名的職業人脈服務商LinkedIn 領英的創始人。曾任PayPal的創始董事】
《閃電式擴張》將會納入鈦媒體Pro版書庫,敬請大家關注前沿書庫的上新動態~每位Pro專業用戶一年可以在書庫中任意選擇三本書,由鈦媒體免費贈送哦~點擊鏈接、登錄,進入“前沿書庫”選書:http://www.tmtpost.com/pro
更多精彩內容,關注鈦媒體微信號(ID:taimeiti),或者下載鈦媒體App
*請認真填寫需求信息,我們會在24小時內與您取得聯系。