成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

.NET、Mono與Java、C++性能測試大PK

原創
開發 后端 新聞
四年一屆的世界杯終于落下帷幕,四支強隊為金杯曾展開最后的爭奪。而在開發領域,.NET、Mono與Java、C++之間的爭論似乎一直沒有停過,本文將為大家做一個橫向的對比,看看誰的性能更好。

【51CTO外電頭條】任何計算設備硬件資源都是有限的,越多的程序和服務競爭資源,用戶的體驗越糟糕(通常表現為延遲較長),性能下降的部分原因是因為安裝了不需要的組件,還有部分原因是程序內部的設計問題,如讓程序隨系統啟動而啟動,或不管你是否會使用它,都讓它在后臺運行著,這些運行著但又未使用的進程都會搶占有限的系統資源。

雖然我見過一些有關程序性能測試的文章,但卻未見過對程序的啟動時間進行測試的,更別說是不同編程語言(框架),或同一框架的不同版本了,但這種測試結果對于選擇特定硬件系統后,確定編程語言是非常有幫助的。本文將介紹當前比較流行的語言(框架) -.NETJava,Mono和C++程序的啟動性能對比,所有測試都是在它們各自的默認設置下進行的。但.NET,Mono,Java托管代碼和C++原生代碼誰的啟動時間最短,誰的性能***呢?首先來看一下熱啟動的對比結果吧!

熱啟動性能對比

圖 1 Mono,Java,.NET和C++程序熱啟動性能對比(值越小越好)

由于測試中有諸多因素會影響結果,為了使測試結果顯得更公平,我們只使用了一些簡單的,可重復的測試,所有語言都可執行這些測試。

首先我們要測試的是從進程創建到進入main函數所花的時間,簡稱為“啟動時間”,要精確地測試出啟動時間是很困難的,有時只有憑用戶的感覺,接下來測量了內存占用情況,內核和用戶消耗的處理器時間。

如何計算啟動時間

在下面的內容中,凡是提到操作系統API,我指的操作系統都是指Windows XP,由于沒有現成的操作系統API可以獲得程序的啟動時間,因此我用了自己發明的方法來計算,我使用了簡單的進程間通信機制來解決這個問題,創建進程時將創建時間作為一個命令行參數傳遞給測試進程,執行到退出代碼時返回當前時間和創建時間的差,具體步驟說明如下:

在調用者進程(BenchMarkStartup.exe)中獲得當前的UTC系統時間;

啟動測試進程,將前面獲得的進程創建時間作為參數傳遞給它;

在分支進程中,獲得main函數開始執行時的當前系統UTC時間;

在同一進程中,計算并調整時間差;

執行到退出代碼時返回時間差;

在調用者進程(BenchMarkStartup.exe)中捕捉退出代碼。

本文會使用到兩個啟動時間:冷啟動時間和熱啟動時間,冷啟動表示系統重啟后,程序的***次啟動時間,熱啟動時間表示程序關閉后,再次啟動所花的時間。冷啟動需要的時間往往會長一些,因為需要加載I/O組件,熱啟動可以利用操作系統的預取功能,因此熱啟動的時間要短得多。

影響性能的因素

對于托管的運行時,與原生代碼比起來,JIT編譯器將會消耗額外的CPU時間和內存。特別是對于冷啟動時間的對比可能會有失公允,C++原生代碼肯定會占有優勢,而托管型的Mono,Java和.NET代碼需要更長的加載時間。另外,如果其它程序加載了你需要的庫,I/O操作也會減少,啟動時間也會得到改善。在Java方面,也有一些啟動加速程序,如Java Quick Starter,Jinitiator,為了公平起見,應該禁用它們。緩存和預取功能也應該留給操作系統去管理,不要浪費不必要的資源。

C++性能測試代碼

C++測試代碼是直接由調用者進程調用的,當它獲得一個命令行參數時,它會將其轉換成__int64來表示FILETIME,其值是從1601/1/1到現在的100 毫微秒間隔數,因此我們可以獲得時間差,以毫秒數返回,用32位大小就足夠了。

  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.    
  4.     FILETIME   ft;  
  5.     GetSystemTimeAsFileTime(&ft);  
  6.     static const __int64 startEpoch2 = 0; // 1601/1/1  
  7.     if( argc < 2 )  
  8.     {  
  9.     ::Sleep(5000);  
  10.     return -1;  
  11.     }  
  12.     FILETIME userTime;  
  13.     FILETIME kernelTime;  
  14.     FILETIME createTime;  
  15.     FILETIME exitTime;  
  16.    
  17.     if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime))  
  18.     {  
  19.       __int64 diff;  
  20.       __int64 *pMainEntryTime = reinterpret_cast<__int64 *>(&ft);  
  21.       _int64 launchTime = _tstoi64(argv[1]);  
  22.       diff = (*pMainEntryTime -launchTime)/10000;      
  23.       return (int)diff;  
  24.     }  
  25.     else 
  26.         return -1;  

下面是創建測試進程的代碼,傳遞給它的是初始時間,返回的是啟動時間。***個調用計算冷啟動時間,后面的調用計算的是熱啟動時間。

  1. DWORD BenchMarkTimes( LPCTSTR szcProg)  
  2. {  
  3.     ZeroMemory( strtupTimes, sizeof(strtupTimes) );  
  4.     ZeroMemory( kernelTimes, sizeof(kernelTimes) );  
  5.     ZeroMemory( preCreationTimes, sizeof(preCreationTimes) );  
  6.     ZeroMemory( userTimes, sizeof(userTimes) );  
  7.     BOOL res = TRUE;  
  8.     TCHAR cmd[100];  
  9.     int i,result = 0;  
  10.     DWORD dwerr = 0;  
  11.     PrepareColdStart();  
  12.     ::Sleep(3000);//3秒延遲  
  13.     for(i = 0; i <= COUNT && res; i++)  
  14.     {  
  15.         STARTUPINFO si;  
  16.         PROCESS_INFORMATION pi;  
  17.         ZeroMemory( &si, sizeof(si) );  
  18.         si.cb = sizeof(si);  
  19.         ZeroMemory( &pi, sizeof(pi) );  
  20.         ::SetLastError(0);  
  21.         __int64 wft = 0;  
  22.         if(StrStrI(szcProg, _T("java")) && !StrStrI(szcProg, _T(".exe")))  
  23.         {  
  24.             wft = currentWindowsFileTime();  
  25.             _stprintf_s(cmd,100,_T("java -client -cp .\\.. %s \"%I64d\""), szcProg,wft);  
  26.         }  
  27.         else if(StrStrI(szcProg, _T("mono")) && StrStrI(szcProg, _T(".exe")))  
  28.         {   
  29.                 wft = currentWindowsFileTime();  
  30.                 _stprintf_s(cmd,100,_T("mono %s \"%I64d\""), szcProg,wft);  
  31.         }  
  32.         else 
  33.         {  
  34.                 wft = currentWindowsFileTime();  
  35.                 _stprintf_s(cmd,100,_T("%s \"%I64d\""), szcProg,wft);           
  36.         }  
  37.    
  38.         // 啟動子進程   
  39.         if( !CreateProcess( NULL,cmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ))   
  40.         {  
  41.             dwerr = GetLastError();  
  42.  _tprintf( _T("CreateProcess failed for '%s' with error code %d:%s.\n"),szcProg, dwerr,GetErrorDescription(dwerr) );  
  43.             return dwerr;  
  44.             //中斷;  
  45.         }  
  46.    
  47.         //等待20秒,或直到子進程退出  
  48.         dwerr = WaitForSingleObject( pi.hProcess, 20000 );  
  49.         if(dwerr != WAIT_OBJECT_0)  
  50.         {  
  51.             dwerr = GetLastError();  
  52.  _tprintf( _T("WaitForSingleObject failed for '%s' with error code %d\n"),szcProg, dwerr );  
  53.             // 關閉進程和線程處理   
  54.             CloseHandle( pi.hProcess );  
  55.             CloseHandle( pi.hThread );  
  56.             break;  
  57.         }  
  58.         res = GetExitCodeProcess(pi.hProcess,(LPDWORD)&result);  
  59.         FILETIME CreationTime,ExitTime,KernelTime,UserTime;  
  60.         if(GetProcessTimes(pi.hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime))  
  61.         {  
  62.             __int64 *pKT,*pUT, *pCT;  
  63.             pKT = reinterpret_cast<__int64 *>(&KernelTime);  
  64.             pUT = reinterpret_cast<__int64 *>(&UserTime);  
  65.             pCT = reinterpret_cast<__int64 *>(&CreationTime);  
  66.             if(i == 0)  
  67.             {  
  68.                 _tprintf( _T("cold start times:\nStartupTime %d ms"), result);  
  69.                 _tprintf( _T(", PreCreationTime: %u ms"), ((*pCT)- wft)/ 10000);   
  70.                 _tprintf( _T(", KernelTime: %u ms"), (*pKT) / 10000);  
  71.                 _tprintf( _T(", UserTime: %u ms\n"), (*pUT) / 10000);   
  72.                 _tprintf( _T("Waiting for statistics for %d warm samples"), COUNT);   
  73.             }  
  74.             else 
  75.             {  
  76.                 _tprintf( _T("."));  
  77.                 kernelTimes[i-1] = (int)((*pKT) / 10000);  
  78.                 preCreationTimes[i-1] = (int)((*pCT)- wft)/ 10000;   
  79.                 userTimes[i-1] = (int)((*pUT) / 10000);  
  80.                 strtupTimes[i-1] = result;      
  81.             }  
  82.         }  
  83.         else 
  84.         {  
  85.             printf( "GetProcessTimes failed for %p", pi.hProcess );   
  86.         }  
  87.         // 關閉進程和線程處理   
  88.         CloseHandle( pi.hProcess );  
  89.         CloseHandle( pi.hThread );  
  90.         if((int)result < 0)  
  91.         {  
  92.             _tprintf( _T("%s failed with code %d: %s\n"),cmd, result,GetErrorDescription(result) );  
  93.             return result;  
  94.         }  
  95.         ::Sleep(1000); //1秒延時  
  96.     }  
  97.     if(i <= COUNT )  
  98.     {  
  99.        _tprintf( _T("\nThere was an error while running '%s', last error code = %d\n"),cmd,GetLastError());  
  100.        return result;  
  101.     }  
  102.     double median, mean, stddev;  
  103.     if(CalculateStatistics(&strtupTimes[0], COUNT, median, mean, stddev))  
  104.     {  
  105.         _tprintf( _T("\nStartupTime: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  106.             mean,median,stddev);  
  107.     }  
  108.     if(CalculateStatistics(&preCreationTimes[0], COUNT, median, mean, stddev))  
  109.     {  
  110.         _tprintf( _T("PreCreation: mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  111.             mean,median,stddev);  
  112.     }  
  113.     if(CalculateStatistics(&kernelTimes[0], COUNT, median, mean, stddev))  
  114.     {  
  115.         _tprintf( _T("KernelTime : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  116.             mean,median,stddev);  
  117.     }  
  118.     if(CalculateStatistics(&userTimes[0], COUNT, median, mean, stddev))  
  119.     {  
  120.         _tprintf( _T("UserTime   : mean = %6.2f ms, median = %3.0f ms, standard deviation = %6.2f ms\n"),   
  121.             mean,median,stddev);  
  122.     }  
  123.    
  124.     return GetLastError();  

注意啟動Mono和Java程序的命令行與.NET或原生代碼有些不同,我也沒有使用性能監視計數器。

如果你想知道我為什么沒有使用GetProcessTimes提供的創建時間,我可以告訴你有兩個原因。首先,對于.NET和Mono,需要DllImport,對于Java需要JNI,這樣就使程序變得更加臃腫了;第二個原因是我發現創建時間不是CreateProcess API被調用的真正時間。從本地硬盤運行測試時,由這兩個因素引起的時間會相差0-10毫秒,如果是從網絡驅動器運行,時間會有數百毫秒的出入,如果是從軟盤上運行,甚至可能達到幾秒。我把這個時間差叫做預創建時間,我猜測這是因為操作系統沒有考慮創建新進程時,從存儲介質讀取文件所花的時間所致,因為只在冷啟動時有這個差異,而熱啟動就沒有。

#p#

.NET和Mono C#性能測試代碼

[[12874]]

在調用的.NET代碼中計算啟動時間和C++有點不同,它使用了DateTime中的FromFileTimeUtc輔助方法。

  1. private const long TicksPerMiliSecond = TimeSpan.TicksPerSecond / 1000;  
  2. static int Main(string[] args)  
  3. {  
  4.     DateTime mainEntryTime = DateTime.UtcNow;//100 nanoseconds units since 1601/1/1  
  5.     int result = 0;  
  6.    
  7.     if (args.Length > 0)  
  8.     {  
  9.         DateTime launchTime = System.DateTime.FromFileTimeUtc(long.Parse(args[0]));  
  10.         long diff = (mainEntryTime.Ticks - launchTime.Ticks) / TicksPerMiliSecond;  
  11.         result = (int)diff;  
  12.    
  13.     }  
  14.     else 
  15.     {  
  16.         System.GC.Collect(2, GCCollectionMode.Forced);  
  17.         System.GC.WaitForPendingFinalizers();  
  18.         System.Threading.Thread.Sleep(5000);  
  19.     }  
  20.     return result;  

使用Mono

要使用Mono必須先從這里下載并安裝好Mono,然后修改環境變量PATH,增加C:\PROGRA~1\MONO-2~1.4\bin\,注意你使用的Mono版本號可能會有些不同,另外,安裝時可以不選中GTK#和XSP組件,因為本次測試用不著它們,為了簡化編譯操作,我特意寫了一個buildMono.bat批處理文件,已包含在本文提供的下載包中。

使用更多.NET版本

我還包括了1.1,2.0,3.5和4.0版本的C# Visual Studio項目,如果你只需運行二進制文件,需要下載和安裝對應的運行時,生成(Build)時需要Visual Studio 2003和Visual Studio 2010,或如果你喜歡使用命令生成,還需要特定的SDK。為了強制加載目標運行時版本,我為所有.NET執行文件創建了配置文件,內容如下,不同的地方就是版本號:

  1. <?xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3. <startup> 
  4.  <supportedRuntime version="v1.1.4322" /> 
  5. </startup> 
  6. </configuration> 

Java性能測試代碼

[[12875]]

首先要從這里下載并安裝Java SDK,同樣也需要向PATH環境變量添加Java路徑,在開始生成前,還需要設置javac.exe的編譯路徑,如:

  1. set path=C:\Program Files\Java\jdk1.6.0_16\bin;%path% 
在本文提供的壓縮包中,我提供了一個buildJava.bat批處理文件來幫助你完成生成操作,Java性能測試代碼如下:
 
  1. public static void main(String[] args)  
  2. {  
  3.     long mainEntryTime = System.currentTimeMillis();//miliseconds since since 1970/1/1  
  4.     int result = 0;  
  5.     if (args.length > 0)  
  6.     {  
  7.         //FileTimeUtc adjusted for java epoch  
  8.         long fileTimeUtc = Long.parseLong(args[0]);//100 nanoseconds units since 1601/1/1  
  9. long launchTime = fileTimeUtc - 116444736000000000L;//100 nanoseconds units since 1970/1/1  
  10.         launchTime /= 10000;//miliseconds since since 1970/1/1  
  11.         result = (int)(mainEntryTime - launchTime);  
  12.     }  
  13.     else 
  14.     {  
  15.         try 
  16.         {  
  17.             System.gc();  
  18.             System.runFinalization();  
  19.             Thread.sleep(5000);  
  20.         }  
  21.         catch (Exception e)  
  22.         {  
  23.             e.printStackTrace();  
  24.         }  
  25.    
  26.     }  
  27.     java.lang.System.exit(result);  
  28. }  

由于Java缺乏測量持續時間的解決方案,我不得不使用毫秒,其它框架可以提供更細粒度的時間單位,但毫秒在這次的測試中已經夠用了。

獲取內存使用情況和處理器時間

Windows進程有許多層面都會使用內存,我將僅限于測量專用字節,最小工作集和峰值工作集。如果你想知道沒有參數時,調用的進程為什么會等待5秒,現在你應該有答案了。在等待2秒后,調用者將使用下面的代碼測量內存使用情況:

 
  1. BOOL PrintMemoryInfo( const PROCESS_INFORMATION& pi)  
  2. {  
  3.  //wait 2 seconds while the process is sleeping for 5 seconds  
  4.     if(WAIT_TIMEOUT != WaitForSingleObject( pi.hProcess, 2000 ))  
  5.      return FALSE;  
  6.     if(!EmptyWorkingSet(pi.hProcess))  
  7.     printf( "EmptyWorkingSet failed for %x\n", pi.dwProcessId );  
  8.     BOOL bres = TRUE;  
  9.     PROCESS_MEMORY_COUNTERS_EX pmc;  
  10. if ( GetProcessMemoryInfo( pi.hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)) )  
  11.     {  
  12.     printf( "PrivateUsage: %lu KB,", pmc.PrivateUsage/1024 );  
  13.         printf( " Minimum WorkingSet: %lu KB,", pmc.WorkingSetSize/1024 );  
  14.         printf( " PeakWorkingSet: %lu KB\n", pmc.PeakWorkingSetSize/1024 );   
  15.    
  16.     }  
  17.     else 
  18.    {  
  19.     printf( "GetProcessMemoryInfo failed for %p", pi.hProcess );   
  20.     bres = FALSE;  
  21.    }  
  22.     return bres;  

最小工作集是調用的進程占用的內存由EmptyWorkingSet API收縮后,我計算出的一個值。

測試結果

這些測試產生的結果很多,我只挑選了與本文主題相關的一些數據,并將熱啟動的測試結果也一并展示出來了,如圖1所示。如果你以調試模式執行測試,產生的結果會更多,對于熱啟動,我執行了9次測試,而冷啟動只有一次,我只采用了中間值(即去掉了***分和***分),處理器內核和用戶時間被歸結到一塊兒,總稱為CPU時間,下表的結果是來自一臺奔四3.0GHz,2GB內存的Windows XP機器的測試結果。

運行時
冷啟動時間(ms)
冷啟動CPU時間(ms)
熱啟動時間(ms)
熱啟動CPU時間(ms)
專用字節(KB)
最小工作集(KB)
峰值工作集(KB)
.Net 1.1
1844
156
93
93
3244
104
4712
.Net 2.0
1609
93
78
93
6648
104
5008
.Net 3.5
1766
125
93
77
6640
104
4976
.Net 4.0
1595
77
46
77
7112
104
4832
Java 1.6
1407
108
94
92
39084
120
11976
Mono 2.6.4
1484
156
93
92
4288
100
5668
CPP code
140
30
15
15
244
40
808

注意其中.NET 2.0和.NET 4.0的熱啟動時間比熱啟動CPU時間要低,你可能認為這違背了基本的物理定律,但需要注意這里的CPU時間指的是進程的整個生命周期,而啟動時間僅僅指進入到main函數時的時間,通過這我們知道可以通過一些優化提高這些框架的啟動速度,正如你前面看到的,C++由于沒有框架,因此優勢很明顯,調用者進程通過預加載一些通用dll使啟動更快。

我沒有所有運行時的歷史數據,但從.NET各版本的表現來看,越新的版本會通過消耗更多的內存來提速,如下圖所示。

.NET框架不同版本程序熱啟動時性能表現

圖 2 .NET框架不同版本程序熱啟動時性能表現(值越小越好)

為托管運行時使用原生鏡像

除了C++原生代碼外,所有運行時都使用了中間代碼,下一步如果可能應該嘗試生成原生鏡像,并再次評估它們的性能,Java沒有一個易于使用的工具來完成這項任務,GCJ只能完成一半的任務,而且它還不是官方運行時的一部分,因此我會忽略它。Mono有一個類似的功能叫做Ahead of Time(AOT),遺憾的是,AOT尚不能在Windows上工作。.NET從一開始就支持原生代碼生成,ngen.exe就是運行時的一部分。

為了方便你,我在本文提供的壓縮包中提供了一個make_nativeimages.bat批處理文件,用它快速生成測試用程序集的原生鏡像。下表展示了.NET框架各版本原生鏡像的測試結果。

運行時
冷啟動時間(ms)
冷啟動CPU時間(ms)
熱啟動時間(ms)
熱啟動CPU時間(ms)
專用字節(KB)
最小工作集(KB)
峰值工作集(KB)
.Net 1.1
2110
140
109
109
3164
108
4364
.Net 2.0
1750
109
78
77
6592
108
4796
.Net 3.5
1859
140
78
77
6588
108
4800
.Net 4.0
1688
108
62
61
7044
104
4184

我們似乎又再次遇到違背物理定律的事情了,上表顯示原生編譯的程序集冷啟動時間更高,不必大驚小怪,因為加載原生鏡像也需要大量的I/O操作,從測試結果來看,它比加載框架所用的時間更多。

運行測試

你可以將測試的可執行文件作為一個參數傳遞給BenchMarkStartup.exe運行一個特殊的測試,對于Java,包名必須匹配目錄結構,因此JavaPerf.StartupTest需要一個..\JavaPerf文件夾。

我在本文提供的壓縮包中提供了一個runall.bat批處理文件,但它無法捕捉現實的冷啟動時間。

如果你想執行真實的測試,你可以手動重啟,或在夜間每隔20-30分鐘調度執行release文件夾的benchmark.bat批處理文件,然后從文本日志文件獲得結果。重啟機器后,它將會運行所有運行時的真實測試。

***的計算機通常會控制CPU頻率以節約能源,但這可能會影響到測試結果,因此在運行測試之前,除了前面我已經提到的事情外,你還必須將電源使用方案設置為“高性能”,以便獲得一致的結果。

小結

如果你有條件下載文后提供的壓縮包按照本文介紹的內容親自做一下對比測試,相信你對托管運行時和原生代碼有更深刻的認識,如果你正在猶豫不決地選擇開發平臺,本文也可以幫助你確定清晰的方向,另外,你還可以參照本文創建其它運行時或UI測試。

本文使用到的測試源代碼和批處理文件從這里下載,我還對Java和Mono專門制作了一個壓縮包,從這里下載。

原文名:Benchmark start-up and system performance for .Net, Mono, Java and C++ native code

【編輯推薦】

  1. 使用ASP.NET 4的自動啟動特性
  2. 詳解.NET 4.0并行計算支持歷史
  3. 詳讀.NET 4.0環境配置
  4. 詳解.NET 4.0中異常處理方面的新特性
  5. 三方面詮釋.NET 4.0的新特性 

 

責任編輯:彭凡 來源: 51CTO
相關推薦

2010-10-20 10:30:58

PHPJSP

2009-09-08 11:24:20

2014-04-17 10:37:43

C++.NET Native

2009-11-06 14:01:48

EPON接入技術

2023-11-21 16:13:38

C++代碼

2021-11-28 08:04:27

.Net性能測試

2020-05-25 17:04:09

AMD云主機評測

2019-07-29 09:19:26

編程語言PythonJava

2012-08-17 10:22:26

HTML5性能瀏覽器

2010-11-01 12:56:02

云計算高性能計算

2012-02-27 09:28:34

HadoopFourinone淘寶

2012-01-04 01:35:46

Power虛擬機x86性能大賽

2024-04-18 11:07:30

C++語言

2010-05-27 12:58:16

MySQL性能測試

2022-08-22 15:32:59

C++C代碼

2020-02-02 15:42:22

PythonC++編程語言

2021-06-10 09:40:12

C++性能優化Linux

2010-01-12 09:48:51

無線交換機

2017-12-13 08:20:15

芯片驍龍?人工智能

2013-07-31 16:56:08

系統級編程語言語言性能語言
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 夜夜操天天操 | 亚洲视频在线观看免费 | 国产福利在线视频 | 国产欧美一区二区精品久导航 | 成人在线播放网站 | 黄色欧美| 在线精品一区二区 | 国产成人免费一区二区60岁 | 精品在线 | 日本免费一区二区三区 | 一级毛片成人免费看a | 波波电影院一区二区三区 | 日本大香伊一区二区三区 | 精品一区二区三区在线观看国产 | 久草热播 | 日韩免费视频一区二区 | 成人免费网站 | 91在线观看视频 | 欧美成视频 | 欧美精品在线一区二区三区 | 成人在线免费视频 | 精品久久久久久久人人人人传媒 | 亚洲精品一区在线观看 | 一级美国黄色片 | 狠狠的干狠狠的操 | 欧美日韩在线观看视频网站 | 欧美二级 | 羞羞网站在线免费观看 | 国产高清精品一区二区三区 | 91精品国产综合久久久久久 | 久久这里只有精品首页 | 91精品国产91久久久久久吃药 | 日韩欧美亚洲 | 日韩a在线 | 久久成人av电影 | 伊人久久综合 | 国产欧美一区二区三区日本久久久 | 成人亚洲综合 | 国产亚洲精品精品国产亚洲综合 | 亚洲综合大片69999 | 久久成人一区 |