成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術文章
文章詳情頁

JavaScript 引擎基礎:原型優化

瀏覽:154日期:2023-11-10 15:08:32

本文就所有 JavaScript 引擎中常見的一些關鍵基礎內容進行了介紹——這不僅僅局限于 V8 引擎。作為一名 JavaScript 開發者,深入了解 JavaScript 引擎是如何工作的將有助于你了解自己所寫代碼的性能特征。

在前一篇文章中,我們討論了 JavaScript 引擎是如何通過 Shapes 和 Inline Caches 來優化對象與數組的訪問的。本文將介紹優化流程中的權衡與取舍,并對引擎在優化原型屬性訪問方面的工作進行介紹。

原文 JavaScript engine fundamentals: optimizing prototypes ,作者 @Benedikt 和 @Mathias ,譯者 hijiangtao 。以下開始正文。

如果你傾向看視頻演講,請移步 YouTube 查看更多。

1. 優化層級與執行效率的取舍

前一篇文章介紹了現代 JavaScript 引擎通用的處理流程:

JavaScript 引擎基礎:原型優化

我們也指出,盡管從高級抽象層面來看,引擎之間的處理流程都很相似,但他們在優化流程上通常都存在差異。為什么呢? 為什么有些引擎的優化層級會比其他引擎更多? 事實證明,在快速獲取可運行的代碼與花費更多時間獲得最優運行性能的代碼之間存在取舍與平衡。

JavaScript 引擎基礎:原型優化

解釋器可以快速生成字節碼,但字節碼通常效率不高。 相比之下,優化編譯器雖然需要更長的時間,但最終會產生更高效的機器碼。

這正是 V8 在使用的模型。V8 的解釋器叫 Ignition,(就原始字節碼執行速度而言)它是所有引擎中最快的解釋器。V8 的優化編譯器名為 TurboFan,它最終會生成高度優化的機器碼。

JavaScript 引擎基礎:原型優化

啟動延遲與執行速度之間的這些權衡便是一些 JavaScript 引擎決定是否在流程中加入優化層的原因。例如,SpiderMonkey 在解釋器和完整的 IonMonkey 優化編譯器之間添加了一個 Baseline 層:

JavaScript 引擎基礎:原型優化

解釋器可以快速生成字節碼,但字節碼執行起來相對較慢。Baseline 生成代碼需要花費更長的時間,但能提供更好的運行時性能。最后,IonMonkey 優化編譯器花費最長時間來生成機器碼,但該代碼運行起來非常高效。

讓我們通過一個具體的例子,看看不同引擎中的優化流程都有哪些差異。這是一些在循環中會經常重復的代碼。

let result = 0;for (let i = 0; i < 4242424242; ++i) {result += i;}console.log(result);

V8開始在 Ignition 解釋器中運行字節碼。從某些方面來看,代碼是否足夠 hot 由引擎決定,引擎話負責調度 TurboFan 前端,它是 TurboFan 中負責處理集成分析數據和構建代碼在機器層面表示的一部分。這部分結果之后會被發送到另一個線程上的 TurboFan 優化器被進一步優化。

JavaScript 引擎基礎:原型優化

當優化器運行時,V8 會繼續在 Ignition 中執行字節碼。 當優化器處理完成后,我們獲得可執行的機器碼,執行流程便會繼續下去。

SpiderMonkey 引擎也開始在解釋器中運行字節碼。但它有一個額外的 Baseline 層,這意味著比較 hot 的代碼會首先被發送到 Baseline。 Baseline 編譯器在主線程上生成 Baseline 代碼,并在完成后繼續后面的執行。

JavaScript 引擎基礎:原型優化

如果 Baseline 代碼運行了一段時間,SpiderMonkey 最終會激活 IonMonkey 前端,并啟動優化器 - 這與 V8 非常相似。當 IonMonkey 進行優化時,代碼在 Baseline 中會一直運行。當優化器處理完成后,被執行的是優化后的代碼而不是 Baseline 代碼。

Chakra 的架構與 SpiderMonkey 非常相似,但 Chakra 嘗試并行處理更多內容以避免阻塞主線程。Chakra 不在主線程上運行編譯器,而是將不同編譯器可能需要的字節碼和分析數據復制出來,并將其發送到一個專用的編譯器進程。

JavaScript 引擎基礎:原型優化

當代碼準備就緒,引擎便開始運行 SimpleJIT 代碼而不是字節碼。 對于 FullJIT 來說流程也是同樣如此。這種方法的好處是,與運行完整的編譯器(前端)相比,復制所產生的暫停時間通常要短得多。但這種方法的缺點是這種 啟發式復制 可能會遺漏某些優化所需的某些信息,因此它在一定程度上是用代碼質量來換時間的消耗。

在 JavaScriptCore 中,所有優化編譯器都與主 JavaScript 執行 完全并發運行 ;根本沒有復制階段!相反,主線程僅僅是觸發了另一個線程上的編譯作業。然后,編譯器使用復雜的加鎖方式從主線程中獲取到要訪問的分析數據。

JavaScript 引擎基礎:原型優化

這種方法的優點在于它減少了主線程上由 JavaScript 優化引起的抖動。 缺點是它需要處理復雜的多線程問題并為各種操作付出一些加鎖的成本。

我們已經討論過在使用解釋器快速生成代碼或使用優化編譯器生成可高效執行代碼之間的一些權衡。但還有另一個權衡: 內存使用 !為了說明這一點,來看一個兩個數字相加的簡單 JvaScript 程序。

function add(x, y) {return x + y;}add(1, 2);

這是我們使用 V8 中的 Ignition 解釋器為 add 函數生成的字節碼:

StackCheckLdar a1Add a0, [0]Return

不要在意這些字節碼 - 你真的不需要閱讀它。關鍵是它只是 四條指令!

當代碼變得 hot,TurboFan 便會生成以下高度優化的機器碼:

leaq rcx,[rip+0x0]movq rcx,[rcx-0x37]testb [rcx+0xf],0x1jnz CompileLazyDeoptimizedCodepush rbpmovq rbp,rsppush rsipush rdicmpq rsp,[r13+0xe88]jna StackOverflowmovq rax,[rbp+0x18]test al,0x1jnz Deoptimizemovq rbx,[rbp+0x10]testb rbx,0x1jnz Deoptimizemovq rdx,rbxshrq rdx, 32movq rcx,raxshrq rcx, 32addl rdx,rcxjo Deoptimizeshlq rdx, 32movq rax,rdxmovq rsp,rbppop rbpret 0x18

這么 一大堆 代碼,尤其是與四條字節碼相比!通常,字節碼比機器碼更緊湊,特別是優化過的機器碼。但另一方面,字節碼需要解釋器才能執行,而優化過機器碼可以由處理器直接執行。

這就是為什么 JavaScript 引擎不簡單粗暴”優化一切”的主要原因之一。正如我們之前所見,生成優化的機器碼也需要很長時間,而最重要的是,我們剛剛了解到優化的機器碼也需要更多的內存。

JavaScript 引擎基礎:原型優化

小結:JavaScript 引擎之所以具有不同優化層,就在于使用解釋器快速生成代碼或使用優化編譯器生成高效代碼之間存在一個基本權衡。通過添加更多優化層可以讓你做出更細粒度的決策,但是以額外的復雜性和開銷為代價。此外,在優化級別和生成代碼所占用的內存之間也存在折衷。這就是為什么 JavaScript 引擎僅嘗試優化比較 hot 功能的原因所在。

2. 原型屬性訪問優化

之前的文章解釋了 JavaScript 引擎如何使用 Shapes 和 Inline Caches 優化對象屬性加載。回顧一下,引擎將對象的 Shape 與對象值分開存儲。

JavaScript 引擎基礎:原型優化

Shapes 可以實現稱為 Inline Caches 或簡稱 ICs 的優化。通過組合,Shapes 和 ICs 可以加快代碼中相同位置的重復屬性訪問速度。

JavaScript 引擎基礎:原型優化

2.1 Class 和基于原型的編程

既然我們知道如何在 JavaScript 對象上快速進行屬性訪問,那么讓我們看一下最近添加到 JavaScript 中的特性:class。JavaScript 中 class 的語法如下所示:

class Bar {constructor(x) {this.x = x;}getX() {return this.x;}}

盡管這看上去是 JavaScript 中的一個全新概念,但它僅僅是基于原型編程的語法糖:

function Bar(x) {this.x = x;}Bar.prototype.getX = function getX() {return this.x;};

在這里,我們在 Bar.prototype 對象上分配一個 getX 屬性。這與其他任何對象的工作方式完全相同,因為原型只是 JavaScript 中的對象!在基于原型的編程語言(如 JavaScript)中,方法通過原型共享,而字段則存儲在實際的實例上。

讓我們來實際看看,當我們創建一個名為 foo 的 Bar 新實例時,幕后所發生的事情。

const foo = new Bar(true);

通過運行此代碼創建的實例具有一個帶有屬性 “x” 的 shape。 foo 的原型是屬于 class Bar 的 Bar.prototype 。

JavaScript 引擎基礎:原型優化

Bar.prototype 有自己的 shape,其中包含一個屬性 ’getX’ ,取值則是函數 getX ,它在調用時只返回 this.x 。 Bar.prototype 的原型是 Object.prototype ,它是 JavaScript 語言的一部分。由于 Object.prototype 是原型樹的根節點,因此它的原型是 null 。

JavaScript 引擎基礎:原型優化

如果你在這個類上創建另一個實例,那么兩個實例將共享對象 shape。兩個實例都指向相同的 Bar.prototype 對象。

2.2 原型屬性訪問

好的,現在我們知道當我們定義一個類并創建一個新實例時會發生什么。但是如果我們在一個實例上調用一個方法會發生什么,比如我們在這里做了什么?

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const x = foo.getX();//^^^^^^^^^^

你可以將任何方法調用視為兩個單獨的步驟:

const x = foo.getX();// is actually two steps:const $getX = foo.getX;const x = $getX.call(foo);

第1步是加載這個方法,它只是原型上的一個屬性(其值恰好是一個函數)。第2步是使用實例作為 this 值來調用該函數。讓我們來看看第一步,即從實例 foo 中加載方法 getX 。

JavaScript 引擎基礎:原型優化

引擎從 foo 實例開始,并且意識到 foo 的 shape 上沒有 ’getX’ 屬性,所以它必須向原型鏈追溯。我們到了 Bar.prototype ,查看它的原型 shape,發現它在偏移0處有 ’getX’ 屬性。我們在 Bar.prototype 的這個偏移處查找該值,并找到我們想要的 JSFunction getX 。就是這樣!

但 JavaScript 的靈活性使得我們可以改變原型鏈鏈接,例如:

const foo = new Bar(true);foo.getX();// → trueObject.setPrototypeOf(foo, null);foo.getX();// → Uncaught TypeError: foo.getX is not a function

在這個例子中,我們調用 foo.getX() 兩次,但每次它都具有完全不同的含義和結果。 這就是為什么盡管原型只是 JavaScript 中的對象,但優化原型屬性訪問對于 JavaScript 引擎而言比優化常規對象的屬性訪問更具挑戰性的原因了。

粗略的來看,加載原型屬性是一個非常頻繁的操作:每次調用一個方法時都會發生這種情況!

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const x = foo.getX();//^^^^^^^^^^

之前,我們討論了引擎如何通過使用 Shapes 和 Inline Caches 來優化訪問常規屬性的。 我們如何在具有相似 shape 的對象上優化原型屬性的重復訪問呢? 我們在上面已經看過是如何訪問屬性的。

JavaScript 引擎基礎:原型優化

為了在這種特殊情況下實現快速重復訪問,我們需要知道這三件事:

foo 的 shape 不包含 ’getX’ 并且沒有改變過。這意味著沒有人通過添加或刪除屬性或通過更改其中一個屬性來更改對象 foo 。 foo 的原型仍然是最初的 Bar.prototype 。這意味著沒有人通過使用 Object.setPrototypeOf() 或通過賦予特殊的 _proto_ 屬性來更改 foo 的原型。 Bar.prototype 的形狀包含 ’getX’ 并且沒有改變。這意味著沒有人通過添加或刪除屬性或更改其中一個屬性來更改 Bar.prototype 。

一般情況下,這意味著我們必須對實例本身執行1次檢查,并對每個原型進行2次檢查,直到找到我們正在尋找的屬性所在原型。 1 + 2N 次檢查(其中 N 是所涉及的原型的數量)對于這種情況聽起來可能不太糟糕,因為這里原型鏈相對較淺 - 但是引擎通常必須處理更長的原型鏈,就像常見的 DOM 類一樣。這是一個例子:

const anchor = document.createElement(’a’);// → HTMLAnchorElementconst title = anchor.getAttribute(’title’);

我們有一個 HTMLAnchorElement ,在其上調用 getAttribute() 方法。這個簡單的錨元素原型鏈就已經涉及6個原型!大多數有趣的 DOM 方法并不是直接存在于 HTMLAnchorElement 原型中,而是在原型鏈的更高層。

JavaScript 引擎基礎:原型優化

我們可以在 Element.prototype 上找到 getAttribute() 方法。這意味著我們每次調用 anchor.getAttribute() 時,JavaScript引擎都需要……

’getAttribute’HTMLAnchorElement.prototypeHTMLElement.prototype’getAttribute’Element.prototype’getAttribute’

總共有7次檢測!由于這是 Web 上一種非常常見的代碼,因此引擎會應用技巧來減少原型上屬性加載所需的檢查次數。

回到前面的例子,我們在 foo 上訪問 ’getX’ 時總共執行了3次檢查:

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const $getX = foo.getX;

在直到我們找到攜帶目標屬性的原型之前,我們需要對原型鏈上的每個對象進行 shape 的缺失檢查。如果我們可以通過將原型檢查折疊到缺失檢查來減少檢查次數,那就太好了。而這基本上就是引擎所做的: 引擎將原型鏈在 Shape 上,而不是直接鏈在實例上。

JavaScript 引擎基礎:原型優化

每個 shape 都指向原型。這也意味著每次 foo 原型發生變化時,引擎都會轉換到一個新 shape。 現在我們只需要檢查一個對象的 shape,這樣既可以斷言某些屬性的缺失,也可以保護原型鏈鏈接。

通過這種方法,我們可以將檢查次數從 1 + 2N 降到 1 + N ,以便在原型上更快地訪問屬性。但這仍相當昂貴,因為它在原型鏈的長度上仍然是線性的。 為了進一步將檢查次數減少到一個常量級別,引擎采用了不同的技巧,特別是對于相同屬性訪問的后續執行。

2.3 Validity cells

V8專門為此目的處理原型的 shape。每個原型都具有一個不與其他對象(特別是不與其他原型共享)共享且獨特的 shape,且每個原型的 shape 都具有與之關聯的一個特殊 ValidityCell 。

JavaScript 引擎基礎:原型優化

只要有人更改相關原型或其祖先的任何原型,此 ValidityCell 就會失效。讓我們來看看它是如何工作的。

為了加速原型的后續訪問,V8 建立了一個 Inline Cache,其中包含四個字段:

JavaScript 引擎基礎:原型優化

在第一次運行此代碼預熱 inline cache 時,V8 會記住目標屬性在原型中的偏移量,找到屬性的原型(本例中為 Bar.prototype ),實例的 shape(在這種情況下為 foo 的 shape),以及與實例 shape 鏈接的 直接原型 中 ValidityCell 的鏈接(在本例中也恰好是 Bar.prototype )。

下次 inline cache 命中時,引擎必須檢查實例的 shape 和 ValidityCell 。如果它仍然有效,則引擎可以直接到達 Prototype 上的 Offset 位置,跳過其他查找。

JavaScript 引擎基礎:原型優化

當原型改變時,shape 將重新分配,且先前的 ValidityCell 失效。因此,Inline Cache 在下次執行時會失效,從而導致性能下降。

回到之前的 DOM 示例,這意味著對 Object.prototype 的任何更改不僅會使 Object.prototype 本身的 inline cache 失效,而且還會使其下游的所有原型失效,包括 EventTarget.prototype , Node.prototype , Element.prototype 等,直到 HTMLAnchorElement.prototype 為止。

JavaScript 引擎基礎:原型優化

實際上,在運行代碼時修改 Object.prototype 意味著完全拋棄性能上的考慮。不要這樣做!

讓我們用一個具體的例子來探討這個問題。 假設我們有一個類叫做 Bar ,并且我們有一個函數 loadX ,它調用 Bar 對象上的方法。 我們用同一個類的實例多調用這個 loadX 函數幾次。

class Bar { /* … */ }function loadX(bar) {return bar.getX(); // IC for ’getX’ on `Bar` instances.}loadX(new Bar(true));loadX(new Bar(false));// IC in `loadX` now links the `ValidityCell` for// `Bar.prototype`.Object.prototype.newMethod = y => y;// The `ValidityCell` in the `loadX` IC is invalid// now, because `Object.prototype` changed.

loadX 中的 inline cache 現在指向 Bar.prototype 的 ValidityCell 。 如果你之后執行了類似于改變 Object.prototype (這是 JavaScript 中所有原型的根節點)的操作,則 ValidityCell 將失效,且現有的 inline cache 會在下次命中時丟失,從而導致性能下降。

修改 Object.prototype 被認為是一個不好的操作,因為它使引擎在此之前為原型訪問準備的所有 inline cache 都失效。 這是另一個 不推薦 的例子:

Object.prototype.foo = function() { /* … */ };// Run critical code:someObject.foo();// End of critical code.delete Object.prototype.foo;

我們擴展了 Object.prototype ,它使引擎在此之前存儲的所有原型 inline cache 均無效了。然后我們運行一些用到新原型方法的代碼。引擎此時則需要從頭開始,并為所有原型屬性的訪問設置新的 inline cache。最后,我們刪除了之前添加的原型方法。

清理,這聽起來像個好主意,對吧?然而在這種情況下,它只會讓情況變得更糟!刪除屬性會修改 Object.prototype ,因此所有 inline cache 會再次失效,而引擎又必須從頭開始。

總結:雖然原型只是對象,但它們由 JavaScript 引擎專門處理,以優化在原型上查找方法的性能表現。把你的原型放在一旁!或者,如果你確實需要修改原型,請在其他代碼運行之前執行此操作,這樣至少不會讓引擎所做的優化付諸東流。

5. Take-aways

我們已經了解了 JavaScript 引擎是如何存儲對象與類的, Shapes 、 Inline Caches 和 ValidityCells 是如何幫助優化原型的。基于這些知識,我們認為存在一個實用的 JavaScript 編碼技巧,可以幫助提高性能:不要隨意修改原型對象(即便你真的需要,那么請在其他代碼運行之前做這件事)。

(完)

來自:https://hijiangtao.github.io/2018/08/21/Prototypes/

標簽: JavaScript
相關文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
不卡的电视剧免费网站有什么| 日韩视频一区二区在线观看| 69堂国产成人免费视频| 亚洲国产日韩av| 亚洲激情视频| 日韩美女视频19| 亚洲成人自拍视频| 亚洲色图一区二区三区| 狠久久av成人天堂| 中文字幕视频一区二区三区久| 欧美女人交a| 国产精品美女久久久久高潮| 欧美精品国产一区二区| 日本一区二区三区在线不卡| 欧美精品一区二区视频| 中文字幕巨乱亚洲| 欧美涩涩视频| 国产精品久久久久aaaa樱花| 亚洲激情在线| 一级日本不卡的影视| 国产日韩亚洲欧美精品| 国产清纯在线一区二区www| 欧美1区视频| 国产精品超碰97尤物18| 亚洲免费黄色| 亚洲超丰满肉感bbw| 日本高清成人免费播放| 久久99国产精品麻豆| 91麻豆精品国产综合久久久久久| 国产成人午夜99999| 精品国产成人系列| 99re8在线精品视频免费播放| 久久日韩精品一区二区五区| 你懂的网址国产 欧美| 国产精品你懂的| 91久久午夜| 天堂av在线一区| 欧美日韩成人激情| 粉嫩aⅴ一区二区三区四区| 久久久久久久综合色一本| 欧美日韩亚洲免费| 一区二区三区丝袜| 91福利区一区二区三区| 国产成人精品一区二| 国产亚洲1区2区3区| 亚洲经典在线| 亚洲成av人片一区二区三区| 日本韩国一区二区| 国产成人av一区二区三区在线| 精品久久久久久综合日本欧美| 欧美成人首页| 一区二区三区**美女毛片| 色哟哟国产精品| 国产iv一区二区三区| 国产精品久久久久三级| 麻豆久久精品| 国产精品456露脸| 国产日韩v精品一区二区| 国产区二精品视| 精品影视av免费| 久久久久免费观看| 亚洲精品影院| 免费观看久久久4p| 26uuuu精品一区二区| 99在线观看免费视频精品观看| 琪琪久久久久日韩精品| 久久这里只有精品6| 日韩亚洲一区在线播放| 捆绑变态av一区二区三区| 欧美性感一区二区三区| 欧美有码视频| 三级影片在线观看欧美日韩一区二区| 91精品国产福利在线观看| 欧美三区在线| 美女精品自拍一二三四| 国产亚洲女人久久久久毛片| 亚洲一区二区三区精品动漫| 国产精品综合在线视频| 国产精品国产三级国产普通话三级 | 国产精品福利电影一区二区三区四区| 亚洲欧美日韩精品久久久| 国产99久久久国产精品| 亚洲人成精品久久久久| 欧美高清hd18日本| 红桃视频国产精品| 九九精品视频在线看| 国产精品热久久久久夜色精品三区| 色呦呦网站一区| 欧美久色视频| 激情五月播播久久久精品| 亚洲欧美在线aaa| 在线综合+亚洲+欧美中文字幕| 欧美日韩精品免费观看视频完整| 日韩精品电影一区亚洲| 欧美激情在线一区二区| 欧美色精品天天在线观看视频| 国产一区二区在线观看免费播放| 蜜臀va亚洲va欧美va天堂| 国产精品色眯眯| 欧美性感一区二区三区| 亚洲夫妻自拍| 成人综合在线视频| 午夜欧美2019年伦理| 国产婷婷一区二区| 欧美日韩一区小说| 亚洲欧洲另类| 成人爱爱电影网址| 日本vs亚洲vs韩国一区三区| 国产精品毛片久久久久久| 91精品婷婷国产综合久久| 国产精品一区免费观看| 91亚洲国产成人精品一区二区三| 亚洲va中文字幕| 久久久久久日产精品| 在线免费不卡电影| 亚洲毛片播放| 99久久免费国产| 激情文学综合插| 亚洲国产精品影院| 国产精品久久久久久久蜜臀 | 一区二区三区在线观看国产| 精品裸体舞一区二区三区| 一本久道久久综合中文字幕 | 在线电影院国产精品| 国产欧美69| 91老师国产黑色丝袜在线| 久久99久国产精品黄毛片色诱| 一区二区三区精品视频| 久久噜噜亚洲综合| 欧美日韩午夜在线视频| 国产精品永久| 欧美体内she精视频在线观看| 国产精品18久久久久久vr| 图片区小说区国产精品视频| 综合久久国产九一剧情麻豆| 欧美成人精品1314www| 欧美日韩亚洲不卡| 老**午夜毛片一区二区三区| 99精品99久久久久久宅男| 99久久精品99国产精品| 国产精品一色哟哟哟| 日本vs亚洲vs韩国一区三区 | 色婷婷综合久色| 亚洲看片网站| 欧美午夜精品久久久久免费视| 成人性视频免费网站| 黄页视频在线91| 秋霞午夜av一区二区三区| 亚洲国产精品一区二区久久恐怖片 | 最新日韩av在线| 久久女同互慰一区二区三区| 欧美一区二区高清| 亚洲欧美不卡| 国产区日韩欧美| 亚洲理伦在线| 在线视频观看日韩| 国产精品xvideos88| 欧美一区久久| av中文字幕不卡| 成人午夜激情在线| 高清成人在线观看| 国产成人亚洲精品狼色在线| 激情文学综合插| 精品中文av资源站在线观看| 免费观看日韩av| 美国十次综合导航| 奇米一区二区三区| 蜜桃视频在线一区| 日韩av中文在线观看| 日本在线观看不卡视频| 免费观看一级特黄欧美大片| 美女性感视频久久| 久久er精品视频| 精品亚洲国内自在自线福利| 久草中文综合在线| 国产一区 二区| 粉嫩av一区二区三区| caoporn国产一区二区| 欧美一区网站| 亚洲午夜精品久久久久久浪潮| 在线观看成人一级片| 亚洲欧洲一区二区天堂久久| 99视频+国产日韩欧美| 亚洲一区日韩在线| 午夜亚洲性色视频| 色屁屁一区二区| 欧美日韩国产精品自在自线| 69堂精品视频| 精品国产在天天线2019| 国产亚洲欧洲一区高清在线观看| 欧美国产乱子伦| 亚洲私人黄色宅男| 亚洲午夜羞羞片| 奇米精品一区二区三区在线观看| 久久激情五月婷婷| 成人免费看的视频| 欧美色综合网| 国产亚洲欧美另类一区二区三区| 色婷婷综合视频在线观看| 3d成人h动漫网站入口|