精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何更快地將String轉換成Int/Long

開發 前端
在很多追求性能的程序挑戰賽中,經常會遇到一個操作:將 String 轉換成 Integer/Long。如果你沒有開發過高并發的系統,或者沒有參加過任何性能挑戰賽,可能會有這樣的疑問:這有啥好講究的,Integer.valueOf/Long.valueOf 又不是不能用。

 [[420585]]

你好鴨,Kirito 今天又來分享性能優化的騷操作了。

在很多追求性能的程序挑戰賽中,經常會遇到一個操作:將 String 轉換成 Integer/Long。如果你沒有開發過高并發的系統,或者沒有參加過任何性能挑戰賽,可能會有這樣的疑問:這有啥好講究的,Integer.valueOf/Long.valueOf 又不是不能用。實際上,很多內置的轉換工具類只滿足了功能性的需求,在高并發場景下,可能會是熱點方法,成為系統性能的瓶頸。

文章開頭,我先做一下說明,本文的測試結論出自:https://kholdstare.github.io/technical/2020/05/26/faster-integer-parsing.html 。測試代碼基于 C++,我會在翻譯原文的同時,添加了部分自己的理解,以協助讀者更好地理解其中的細節。

問題提出

假設現在有一些文本信息,固定長度為 16 位,例如下文給出的時間戳,需要盡可能快地解析這些時間戳

  1. timestamp 
  2. 1585201087123567 
  3. 1585201087123585 
  4. 1585201087123621 

方法體如下所示:

  1. std::uint64_t parse_timestamp(std::string_view s) 
  2.   // ??? 

問題提出后,大家不妨先思考下,如果是你,你會采取什么方案呢?帶著這樣的思考,我們進入下面的一個個方案。

Native 方案

我們有哪些現成的轉換方案呢?

  • 繼承自 C 的 std::atoll
  • std::stringstream
  • C++17 提供的 charconv
  • boost::spirit::qi

評測程序采用 Google Benchmark 進行對比評測。同時,我們以不做任何轉換的方案來充當 baseline,以供對比。(baseline 方案在底層,相當于將數值放進來了寄存器中,所以命名成了 BM_mov)

下面給出的評測代碼不是那么地關鍵,只是為了給大家展示評測是如何運行的。

  1. static void BM_mov(benchmark::State& state) { 
  2.   for (auto _ : state) { 
  3.     benchmark::DoNotOptimize(1585201087123789); 
  4.   } 
  5.  
  6. static void BM_atoll(benchmark::State& state) { 
  7.   for (auto _ : state) { 
  8.     benchmark::DoNotOptimize(std::atoll(example_timestamp)); 
  9.   } 
  10.  
  11. static void BM_sstream(benchmark::State& state) { 
  12.   std::stringstream s(example_timestamp); 
  13.   for (auto _ : state) { 
  14.     s.seekg(0); 
  15.     std::uint64_t i = 0; 
  16.     s >> i; 
  17.     benchmark::DoNotOptimize(i); 
  18.   } 
  19. static void BM_charconv(benchmark::State& state) { 
  20.   auto s = example_timestamp; 
  21.   for (auto _ : state) { 
  22.     std::uint64_t result = 0; 
  23.     std::from_chars(s.data(), s.data() + s.size(), result); 
  24.     benchmark::DoNotOptimize(result); 
  25.   } 
  26.  
  27. static void BM_boost_spirit(benchmark::State& state) { 
  28.   using boost::spirit::qi::parse; 
  29.   for (auto _ : state) { 
  30.     std::uint64_t result = 0; 
  31.     parse(s.data(), s.data() + s.size(), result); 
  32.     benchmark::DoNotOptimize(result); 
  33.   } 

Native

可以發現 stringstream 表現的非常差。當然,這并不是一個公平的比較,但從測評結果來看,使用 stringstream 來實現數值轉換相比 baseline 慢了 391 倍。相比之下, 和 boost::spirit 表現的更好。

既然我們已經知道了目標字符串包含了要解析的數字,而且不需要做任何的數值校驗,基于這些前提,我們可以思考下,還有更快的方案嗎?

Naive 方案

我們可以通過一個再簡單不過的循環方案,一個個地解析字符。

  1. inline std::uint64_t parse_naive(std::string_view s) noexcept 
  2.   std::uint64_t result = 0; 
  3.   for(char digit : s) 
  4.   { 
  5.     result *= 10; 
  6.     result += digit - '0'
  7.   } 
  8.   return result; 

Naive

雖然這層 for 循環看起來呆呆的,但如果這樣一個呆呆的解決方案能夠擊敗標準庫實現,何樂而不為呢?前提是,標準庫的實現考慮了異常場景,做了一些校驗,這種 for 循環寫法的一個前提是,我們的輸入一定是合理的。

之前我的文章也提到過這個方案。顯然, naive 的方案之后還會有更優的替代方案。

循環展開方案

記得我們在文章的開頭加了一個限定,限定了字符串長度固定是 16 位,所以循環是可以被省略的,循環展開之后,方案可以更快。

  1. inline std::uint64_t parse_unrolled(std::string_view s) noexcept 
  2.   std::uint64_t result = 0; 
  3.  
  4.   result += (s[0] - '0') * 1000000000000000ULL; 
  5.   result += (s[1] - '0') * 100000000000000ULL; 
  6.   result += (s[2] - '0') * 10000000000000ULL; 
  7.   result += (s[3] - '0') * 1000000000000ULL; 
  8.   result += (s[4] - '0') * 100000000000ULL; 
  9.   result += (s[5] - '0') * 10000000000ULL; 
  10.   result += (s[6] - '0') * 1000000000ULL; 
  11.   result += (s[7] - '0') * 100000000ULL; 
  12.   result += (s[8] - '0') * 10000000ULL; 
  13.   result += (s[9] - '0') * 1000000ULL; 
  14.   result += (s[10] - '0') * 100000ULL; 
  15.   result += (s[11] - '0') * 10000ULL; 
  16.   result += (s[12] - '0') * 1000ULL; 
  17.   result += (s[13] - '0') * 100ULL; 
  18.   result += (s[14] - '0') * 10ULL; 
  19.   result += (s[15] - '0'); 
  20.  
  21.   return result; 

unrolled

關于循環展開為什么會更快,可以參考我過去關于 JMH 的文章。

byteswap 方案

先思考下,如果繼續圍繞上述的方案進行,我們可能只有兩個方向:

并發執行加法和乘法計算,但這種 CPU 操作似乎又不能通過多線程之類的手段進行加速,該如何優化是個問題

將乘法和加法運算轉換成位運算,獲得更快的 CPU 執行速度,但如果轉換又是個問題

相信讀者們都會有這樣的疑問,那我們繼續帶著這樣疑問往下看原作者的優化思路是什么。

緊接著上述的循環展開方案,將 “1234” 解析為 32 位整數對應的循環展開操作繪制為圖,過程如下:

Unrolled solution graph

我們可以看到,乘法和加法的操作次數跟字符的數量是線性相關的。由于每一次乘法都是由不同的乘數進行,所以我們不能只乘“一次”,在乘法的最后,我們還需要將所有結果相加。乍一看,好像很難優化。

下面的優化技巧,需要一些操作系統、編譯原理相關的知識作為輔助,你需要了解 byteswap 這個系統調用,了解大端序和小端序的字節序表示方法(后面我也會分享相關的文章),如果你不關心這些細節,也可以直接跳到本段的最后,直接看結論。

理解清楚下圖的含義,需要理解幾個概念:

  • 字符 1 對應的 ascii 值是 31,相應的 2 對應 32,4 對應 34
  • 在小端序機器上(例如 x86),字符串是以大端序存儲的,而 Integer 是以小端序存儲的
  • byteswap 可以實現字節序調換

byteswap

上圖展示了十六進制表示下的轉換過程,可以在更少的操作下達到最終的解析狀態。

將上圖的流程使用 C++ 來實現,將 String 重新解釋為 Integer,必須使用 std::memcpy(避免命名沖突),執行相減操作,然后通過編譯器內置的 __builtin_bswap64 在一條指令中交換字節。到目前為止,這是最快的一個優化。

  1. template <typename T> 
  2. inline T get_zeros_string() noexcept; 
  3.  
  4. template <> 
  5. inline std::uint64_t get_zeros_string<std::uint64_t>() noexcept 
  6.   std::uint64_t result = 0; 
  7.   constexpr char zeros[] = "00000000"
  8.   std::memcpy(&result, zeros, sizeof(result)); 
  9.   return result; 
  10.  
  11. inline std::uint64_t parse_8_chars(const char* string) noexcept 
  12.   std::uint64_t chunk = 0; 
  13.   std::memcpy(&chunk, string, sizeof(chunk)); 
  14.   chunk = __builtin_bswap64(chunk - get_zeros_string<std::uint64_t>()); 
  15.  
  16.   // ... 

我們看上去得到了想要的結果,但是這個方案從時間復雜度來看,仍然是 O(n) 的,是否可以在這個方案的基礎上,繼續進行優化呢?

分治方案

從最初的 Native 方案,到上一節的 byteswap 方案,我們都只是優化了 CPU 操作,并沒有優化復雜度,既然不滿足于 O(n),那下一個復雜度可能性是什么?O(logn)!我們可以將每個相鄰的數字組合成一對,然后將每對數字繼續組合成一組四個,依此類推,直到我們得到整個整數。

如何同時處理鄰近的數字,這是讓算法跑進 O(logn) 的關鍵

該方案的關鍵之處在于:將偶數位的數字乘以 10 的冪,并且單獨留下奇數位的數字。這可以通過位掩碼(bitmasking)來實現

分治方案

通過 bitmasking,我們可以一次對多個數字進行操作,將它們組合成一個更大的組合

通過使用這個掩碼技巧來實現前文提到的 parse_8_chars 函數。使用 bitmasking 的另一好處在于,我們不用減去 '0' ,因為位掩碼的副作用,使得我們正好可以省略這一步。

  1. inline std::uint64_t parse_8_chars(const char* string) noexcept 
  2.   std::uint64_t chunk = 0; 
  3.   std::memcpy(&chunk, string, sizeof(chunk)); 
  4.  
  5.   // 1-byte mask trick (works on 4 pairs of single digits) 
  6.   std::uint64_t lower_digits = (chunk & 0x0f000f000f000f00) >> 8; 
  7.   std::uint64_t upper_digits = (chunk & 0x000f000f000f000f) * 10; 
  8.   chunk = lower_digits + upper_digits; 
  9.  
  10.   // 2-byte mask trick (works on 2 pairs of two digits) 
  11.   lower_digits = (chunk & 0x00ff000000ff0000) >> 16; 
  12.   upper_digits = (chunk & 0x000000ff000000ff) * 100; 
  13.   chunk = lower_digits + upper_digits; 
  14.  
  15.   // 4-byte mask trick (works on pair of four digits) 
  16.   lower_digits = (chunk & 0x0000ffff00000000) >> 32; 
  17.   upper_digits = (chunk & 0x000000000000ffff) * 10000; 
  18.   chunk = lower_digits + upper_digits; 
  19.  
  20.   return chunk; 

trick 方案

綜合前面兩節,解析 16 位的數字,我們將它分成兩個 8 字節的塊,運行剛剛編寫的 parse_8_chars,并對其進行基準測試!

  1. inline std::uint64_t parse_trick(std::string_view s) noexcept 
  2.   std::uint64_t upper_digits = parse_8_chars(s.data()); 
  3.   std::uint64_t lower_digits = parse_8_chars(s.data() + 8); 
  4.   return upper_digits * 100000000 + lower_digits; 
  5.  
  6. static void BM_trick(benchmark::State& state) { 
  7.   for (auto _ : state) { 
  8.     benchmark::DoNotOptimize(parse_trick(example_stringview)); 
  9.   } 

trick

看上去優化的不錯,我們將循環展開方案的基準測試優化了近 56% 的性能。能做到這一點,主要得益于我們手動進行一系列 CPU 優化的操作,雖然這些并不是特別通用的技巧。這樣算不算開了個不好的頭呢?我們看起來對 CPU 操作干預地太多了,或許我們應該放棄這些優化,讓 CPU 自由地飛翔。

SIMD trick 方案

你是不是以為上面已經是最終方案了呢?不,優化還剩最后一步。

我們已經得到了一個結論

  • 同時組合多組數字以實現 O(logn) 復雜度

如果有 16 個字符或 128 位的字符串要解析,還可以使用 SIMD。感興趣的讀者可以參考SIMD stands for Single Instruction Multiple Data。Intel 和 AMD CPU 都支持 SSE 和 AVX 指令,并且它們通常使用更寬的寄存器。

SIMA 簡單來說就是一組 CPU 的擴展指令,可以通過調用多組寄存器實現并行的乘法運算,從而提升系統性能。我們一般提到的向量化運算就是 SIMA。

讓我們先設置 16 個字節中的每一個數字:

  1. inline std::uint64_t parse_16_chars(const char* string) noexcept 
  2.   auto chunk = _mm_lddqu_si128( 
  3.     reinterpret_cast<const __m128i*>(string) 
  4.   ); 
  5.   auto zeros =  _mm_set1_epi8('0'); 
  6.   chunk = chunk - zeros; 
  7.    
  8.   // ... 

現在,主角變成了 madd 該系統調用。這些 SIMD 函數與我們使用位掩碼技巧所做的操作完全一樣——它們采用同一個寬寄存器,將其解釋為一個由較小整數組成的向量,每個乘以一個特定的乘數,然后將相鄰位的結果相加到一個更寬的整數向量中。所有操作一步完成。

  1. // The 1-byte "trick" in one instruction 
  2. const auto mult = _mm_set_epi8( 
  3.   1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10 
  4. ); 
  5. chunk = _mm_maddubs_epi16(chunk, mult); 

2 字節方案其實還有另一條指令,但不幸的是我并沒有找到 4 字節方案的指令,還是需要兩條指令。這是完整的 parse_16_chars 方案:

  1. inline std::uint64_t parse_16_chars(const char* string) noexcept 
  2.   auto chunk = _mm_lddqu_si128( 
  3.     reinterpret_cast<const __m128i*>(string) 
  4.   ); 
  5.   auto zeros =  _mm_set1_epi8('0'); 
  6.   chunk = chunk - zeros; 
  7.  
  8.   { 
  9.     const auto mult = _mm_set_epi8( 
  10.       1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10 
  11.     ); 
  12.     chunk = _mm_maddubs_epi16(chunk, mult); 
  13.   } 
  14.   { 
  15.     const auto mult = _mm_set_epi16(1, 100, 1, 100, 1, 100, 1, 100); 
  16.     chunk = _mm_madd_epi16(chunk, mult); 
  17.   } 
  18.   { 
  19.     chunk = _mm_packus_epi32(chunk, chunk); 
  20.     const auto mult = _mm_set_epi16(0, 0, 0, 0, 1, 10000, 1, 10000); 
  21.     chunk = _mm_madd_epi16(chunk, mult); 
  22.   } 
  23.  
  24.   return ((chunk[0] & 0xffffffff) * 100000000) + (chunk[0] >> 32); 

SIMD trick

0.75 nanoseconds! 是不是大吃一驚呢.

總結

整體對比

有人可能會問,你為啥要用 C++ 來介紹下,不能用 Java 嗎?我再補充下,本文的測試結論,均來自于老外的文章,文章出處見開頭,其次,本文的后半部分的優化,都是基于一些系統調用,和 CPU 指令的優化,這些在 C++ 中實現起來方便一些,Java 只能走系統調用。

在最近過去的性能挑戰賽中,由于限定了不能使用 JNI,使得選手們只能將方案止步于循環展開方案,試想一下,如果允許走系統調用,加上比賽中字符串也基本是固定的長度,完全可以采用 SIMD 的 trick 方案,String 轉 Long 的速度會更快。

polardb優化點

實際上,在之前 polarDB 的比賽中,普哥就給我介紹過 bswap 的向量化方案,這也是為啥 Java 方案就是比 C++ 方案遜色的原因之一,C++ 在執行一些 CPU 指令集以及系統調用上,比 Java 方便很多。

如何看待這一系列的優化呢?從 std::stringstream 的 86.23 到 sima trick 方案的 0.75,這個優化的過程是令人興奮的,但我們也發現,越往后,越是用到一些底層的優化技巧,正如方案中的 trick 而言,適用性是有限的。也有一種聲音是在說:花費這么大精力去優化,為啥不去寫匯編呢?這又回到了“優化是萬惡之源”這個話題。在業務項目中,可能你不用過多關注 String 是如何轉換為 Long 和 Integer 的,可能 Integer.valueOf 和 Long.valueOf 就可以滿足你的訴求,但如果你是一個需要大數據解析系統,String 轉換是系統的瓶頸之一,相信本文的方案會給你一定的啟發。

另外對于 SIMD 這些方案,我想再多說一句。其實一些性能挑戰賽進行到最后,大家的整體方案其實都相差無幾,無非是參數差異,因為比賽場景通常不會太復雜,最后前幾名的差距,就是在一些非常小的細節上。正如 SIMA 提供的向量化運算等優化技巧,它就是可以幫助你比其他人快個幾百毫秒,甚至 1~2s。這時候你會感嘆,原來我跟大神的差距,就是在這些細節上。但反觀整個過程,似乎這些優化并不能幫助程序設計競賽發揮更大的能量,一個比賽如果只能依靠 CPU 優化來實現區分度,我覺得一定不是成功的。所以,對于主辦方而言,禁用掉一些類庫,其實有效的避免了內卷,于參賽者而言,算是一種減負了。希望以后的比賽也都朝著讓選手花更多精力去優化方案,而不是優化通用的細節上。

再回到 String 解析成 Long/Integer 的話題上。在實際使用時,大家也不用避諱繼續使用 Integer.valueOf 或者 Long.valueOf,大多數情況下,這不是系統的瓶頸。而如果你恰好在某些場景下遇到了 String 轉換的瓶頸,希望本文能夠幫到你。

 

責任編輯:武曉燕 來源: Kirito的技術分享
相關推薦

2023-10-20 08:00:00

人工智能MusicGen

2020-06-15 11:04:38

JavaScript 代碼JavaScript

2025-08-25 09:15:12

2011-02-25 10:22:03

ibmdwXMLDB2

2011-12-09 21:13:29

iOS

2021-06-07 17:30:23

LinuxASCII圖片轉換

2021-07-14 14:50:08

LinuxASCII圖片

2022-10-12 09:55:14

xls文件xlsx文件

2022-07-19 10:53:57

模型算法智能

2021-03-15 08:00:00

音頻框架數據

2011-08-02 09:46:04

iOS開發 XML

2011-08-02 10:08:32

IOS開發 XML

2019-09-06 08:00:00

開源技術 語音

2023-12-11 09:00:00

人工智能3D模型

2021-10-04 09:25:28

Flutter圖像Web

2021-02-03 21:24:42

Joplin筆記

2017-08-10 14:15:31

Windows10Windows文件轉換

2020-11-14 16:04:17

前端.md文件html文件

2023-11-09 09:00:00

OpenAI人工智能Whisper

2021-03-25 12:50:31

Linux磁盤命令
點贊
收藏

51CTO技術棧公眾號

久久久久中文字幕亚洲精品| 欧美日韩综合精品| 久久在线视频精品| 欧美1区2区3区4区| 欧美色网一区二区| 无码人妻精品一区二区蜜桃网站| www.天天干.com| 亚洲专区一区二区三区| 日韩专区在线观看| 亚洲一区二区三区综合| 99精品国自产在线| 五月婷在线视频| 你懂的亚洲视频| 亚洲图片欧洲图片av| 亚洲天堂伊人网| 日本а中文在线天堂| 亚洲欧美偷拍三级| 欧美极品视频一区二区三区| 国产乱淫片视频| 国产精品视区| 欧美美女18p| 国产真人做爰视频免费| 国产精品白浆| 538在线一区二区精品国产| 18禁免费观看网站| 成人免费高清| 国产亚洲成aⅴ人片在线观看| 国产欧美日韩综合精品一区二区 | 91久久国产婷婷一区二区| 日韩欧美亚洲国产| 欧美久久久久| 色噜噜久久综合伊人一本| 欧产日产国产精品98| 99综合久久| 欧美性猛交xxxx乱大交退制版| 青草青青在线视频| 日韩av毛片| 亚洲日本一区二区| 亚洲一区二区在| 久久视频www| 91日韩在线专区| 国产伦精品一区二区| 国产欧美日韩成人| 久99久精品视频免费观看| 日韩美女在线播放| 国产精品久久久久久99| aa视频在线免费观看| 欧美欧美黄在线二区| 精品国产乱码久久久久久夜甘婷婷 | 国产精品偷伦一区二区| 天堂中文在线网| 国产精品久久久一区二区| 欧美激情2020午夜免费观看| 亚洲视频重口味| 91一区二区三区四区| 中文字幕在线看视频国产欧美在线看完整 | 亚洲香蕉伊综合在人在线视看| 熟妇高潮一区二区| 狼人天天伊人久久| 成人精品视频一区二区三区| 欧美日韩在线免费| 一区二区高清视频| 欧洲不卡视频| 亚洲色欲色欲www在线观看| 在线视频一区观看| 免费高清在线观看| 亚洲欧美日韩国产成人精品影院| 吴梦梦av在线| xvideos国产在线视频| 樱桃视频在线观看一区| 欧美精品在欧美一区二区| 欧美14一18处毛片| 天天综合天天综合色| 黄色免费视频大全| gogo亚洲高清大胆美女人体| 在线精品视频免费观看| 久热精品在线观看视频| 日本少妇xxxx| 欧美系列精品| 日韩一区二区三区观看| 美女伦理水蜜桃4| 天堂99x99es久久精品免费| 亚洲性线免费观看视频成熟| 成人18视频免费69| 欧美日韩国产欧| 26uuu久久噜噜噜噜| 国产又粗又猛又爽又| 精品无人区卡一卡二卡三乱码免费卡| 91在线视频免费| 蜜桃av中文字幕| 国产日韩欧美制服另类| 中文字幕在线亚洲精品| 成人影音在线| 欧美性一级生活| wwwxxx色| 国模私拍一区二区| 激情综合电影网| 国产97免费视| 99热这里只有精品1| 91在线码无精品| 在线观看免费91| 爱啪啪综合导航| 欧美日韩精品系列| 在线xxxxx| 日韩免费视频| 91极品视频在线| 国产又黄又爽视频| 久久久久久久综合色一本| 超碰10000| 深夜视频一区二区| 亚洲第一区中文99精品| 婷婷丁香综合网| 亚洲国产欧美国产综合一区| 国产精品一区专区欧美日韩| av7777777| jizz中国少妇| 91麻豆文化传媒在线观看| 99亚洲国产精品| 91超碰碰碰碰久久久久久综合| 日韩欧美国产麻豆| 后入内射无码人妻一区| 亚洲免费婷婷| 粉嫩精品一区二区三区在线观看| 北条麻妃在线| 福利一区视频在线观看| 日本泡妞xxxx免费视频软件| 久久国产电影| 国产精品吹潮在线观看| 天堂v在线观看| 一区av在线播放| 福利视频999| 欧美偷拍综合| 日韩av电影在线网| 91精品无人成人www| 欧美日韩视频精品二区| 一区二区在线观看视频| 看看黄色一级片| 日韩电影免费网站| 国产成人一区二区三区小说| 视频国产一区二区三区| 亚洲一区二区三区三| 精品国产鲁一鲁一区二区三区| 国产真实有声精品录音| 欧美专区日韩视频| 无码国产精品一区二区色情男同 | 91在线观看下载| 欧美视频在线观看视频| xxxx日韩| 久久久久久久久久久av| 91大神免费观看| 男人av在线| 亚洲精品国产品国语在线app| 亚洲综合激情视频| 国产精品黑丝在线播放| 国产在线观看不卡| 在线视频二区| 欧美精品第1页| 老湿机69福利| 国产不卡视频在线播放| 免费不卡av在线| 神马香蕉久久| 日本中文字幕不卡免费| 黄色影院在线播放| 欧美视频在线播放| 制服丨自拍丨欧美丨动漫丨| 国产一区二区三区高清播放| 精品免费久久久久久久| 超碰成人97| 66m—66摸成人免费视频| 欧美 日韩 国产 精品| 亚洲一级少妇| 亚洲精品久久视频| 精品人妻一区二区三区潮喷在线 | 中国一级片在线观看| 国产久卡久卡久卡久卡视频精品| 精品人妻人人做人人爽| 色综合久久中文| 国产成人久久久| 老司机免费在线视频| 欧美精品一区视频| 中文字幕在线播| 亚洲欧美区自拍先锋| 午夜影院福利社| 久久久天天操| 欧美日韩在线免费观看视频| avtt综合网| 国产精品大陆在线观看| 国产一二区在线| 亚洲国产精品字幕| 在线免费看91| 午夜在线视频| 色爱区综合激月婷婷| 欧美成人久久久免费播放| 国产福利91精品一区| 久久精品免费一区二区| 99精品在线观看| 国产精品区一区二区三在线播放| 播放一区二区| 91精品国产91久久久| 午夜精品一区| 日韩高清中文字幕| av官网在线观看| 一本久久a久久免费精品不卡| 欧美做爰啪啪xxxⅹ性| 2021国产精品久久精品| 99久久综合网| 久久精品国产精品亚洲红杏| 国产素人在线观看| 无码一区二区三区视频| 欧美一区二区三区电影在线观看| 日韩精品视频在线看| 久久久亚洲欧洲日产国码αv| 国产二级片在线观看| 在线精品视频在线观看高清| 热re99久久精品国99热蜜月| 91久久精品无嫩草影院| 国产精品无码专区在线观看 | 日韩av电影国产| 欧美hdxxxx| 日韩在线视频二区| 蜜桃免费在线| 亚洲国产精品一区二区久| 国产麻豆精品一区| 欧美无砖专区一中文字| 国产原创视频在线| 精品久久久久久国产91| 欧美日韩亚洲国产另类| 国产精品久久久久久久久久久免费看| 91视频啊啊啊| 波波电影院一区二区三区| 黄色a级三级三级三级| 蜜臀av在线播放一区二区三区| 欧美伦理91i| 岛国毛片在线观看| 久久综合九色综合97婷婷| 人妻 日韩 欧美 综合 制服| 国产精品一区二区男女羞羞无遮挡| 天天干在线影院| 日韩在线a电影| 亚洲人成色77777| 乱码第一页成人| 日本一极黄色片| 日韩在线一区二区三区| 久久久久久久久久久久久国产精品| 亚洲国内自拍| 免费一级特黄特色毛片久久看| 国产综合精品| 日韩网站在线免费观看| 亚洲黄网站黄| 免费av手机在线观看| 亚洲人成高清| 国产一区二区网| 久久裸体视频| 亚洲一二三区av| 青草av.久久免费一区| 国产精品美女午夜av| 日韩三级小视频| 亚洲激情欧美激情| 久久久久人妻一区精品色欧美| 亚洲欧美韩国综合色| 欧美三级在线免费观看| 亚洲午夜日本在线观看| 精品在线视频免费| 欧美性生交xxxxxdddd| 天干夜夜爽爽日日日日| 欧美三级视频在线| 国产偷拍一区二区| 亚洲国产精彩中文乱码av在线播放| 免费观看黄色av| 亚洲男人天堂久| 亚洲精品传媒| 欧美精品videosex性欧美| 交100部在线观看| 国产精彩精品视频| 国产一区2区在线观看| 国产伦理久久久| 国产欧美日韩在线观看视频| 中文字幕久久综合| 亚洲一级一区| 97神马电影| 乱子伦一区二区三区| 午夜视频久久久久久| 极品国产91在线网站| 欧美日韩国产小视频在线观看| 99在线精品视频免费观看20| 亚洲国内高清视频| 91九色在线porn| 欧美国产日本高清在线 | 激情欧美一区| 久久精品视频91| 国产成人高清在线| 五月天精品视频| 一区二区欧美精品| 高潮无码精品色欲av午夜福利| 日韩欧美美女一区二区三区| 欧美巨乳在线| 欧美精品在线免费| 神马久久资源| 国产成人成网站在线播放青青| 中国av一区| 久久久久久久激情视频| 日操夜操天天操| 偷拍亚洲欧洲综合| 国产三级按摩推拿按摩| 亚洲天堂影视av| av中文字幕在线看| 成人性教育视频在线观看| 亚洲精华一区二区三区| 青青在线免费视频| 日本亚洲天堂网| 粉嫩av懂色av蜜臀av分享| 亚洲六月丁香色婷婷综合久久| 五月婷婷激情视频| 欧美va亚洲va| 老司机精品视频在线观看6| 国产xxx69麻豆国语对白| 成人av资源网址| 日韩国产精品毛片| 蜜桃视频在线观看一区二区| 先锋资源av在线| 一区二区在线观看免费视频播放 | 2020国产在线| 3d动漫精品啪啪一区二区三区免费 | 亚洲免费精彩视频| 污的网站在线观看| 成人在线国产精品| 日韩欧美视频在线播放| av免费网站观看| 久久一区二区三区四区| 粉嫩aⅴ一区二区三区| 欧美va亚洲va国产综合| 羞羞视频在线观看不卡| 成人久久久久久| 91偷拍一区二区三区精品| 亚洲最大综合网| 国产三级一区二区| 九九热最新视频| 国产一区二区动漫| 写真福利精品福利在线观看| 欧美aaaaa喷水| 亚洲欧美高清| 美女洗澡无遮挡| 一本到不卡免费一区二区| 天堂资源最新在线| 欧美在线一级视频| 性欧美lx╳lx╳| 成人在线观看a| 91亚洲天堂| 亚洲另类图片色| 国产高清不卡| 日本视频精品一区| 男女性色大片免费观看一区二区 | 高清一区二区三区四区| 欧美另类在线观看| 国产在线播放精品| 无码播放一区二区三区| 91蝌蚪porny九色| 在线免费观看国产精品| 中文字幕日韩电影| 亚洲一区导航| 日韩欧美一级在线| proumb性欧美在线观看| 亚洲婷婷综合网| 尤物yw午夜国产精品视频明星| 青青伊人久久| 蜜桃视频一区二区在线观看| 成人激情免费网站| www.久久久久久久| 日韩亚洲精品视频| 99精品中文字幕在线不卡| 黄色一级在线视频| 国产丝袜在线精品| 国产免费黄色网址| 韩国精品久久久999| 永久免费无码av网站在线观看| 精品久久久一区| 国产免费av在线| 亚洲影院色无极综合| 国产精品视频久久一区| 91香蕉国产视频| 欧美变态tickle挠乳网站| 欲香欲色天天天综合和网| 亚洲欧洲日韩精品| 成人自拍视频在线| 亚洲精品成人在线视频| 日韩在线观看免费网站| 盗摄牛牛av影视一区二区| 日本999视频| 亚洲老妇xxxxxx| 麻豆影视在线| 97人人模人人爽人人少妇| 视频一区二区三区在线| 成年人av电影| 亚洲另类激情图| 亚洲一区二区电影| 国产一区二区在线免费播放| 亚洲午夜在线电影| 伊人免费在线| 欧美韩国日本精品一区二区三区| 国产精品综合av一区二区国产馆|