diff options
-rw-r--r-- | base/file_util.h | 9 | ||||
-rw-r--r-- | base/file_util_win.cc | 25 | ||||
-rw-r--r-- | chrome/app/client_util.cc | 62 | ||||
-rw-r--r-- | chrome_frame/test/perf/chrome_frame_perftest.cc | 52 |
4 files changed, 101 insertions, 47 deletions
diff --git a/base/file_util.h b/base/file_util.h index 6277748..d52bba9 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -603,6 +603,15 @@ inline bool MakeFileUnreadable(const FilePath& path) { #endif // UNIT_TEST +#if defined(OS_WIN) + // Loads the file passed in as an image section and touches pages to avoid + // subsequent hard page faults during LoadLibrary. The size to be pre read + // is passed in. If it is 0 then the whole file is paged in. The step size + // which indicates the number of bytes to skip after every page touched is + // also passed in. + bool PreReadImage(const wchar_t* file_path, size_t size_to_read, + size_t step_size); +#endif // OS_WIN } // namespace file_util // Deprecated functions have been moved to this separate header file, diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 74b9406..26955f2 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -14,6 +14,7 @@ #include "base/file_path.h" #include "base/logging.h" +#include "base/pe_image.h" #include "base/scoped_comptr_win.h" #include "base/scoped_handle.h" #include "base/string_util.h" @@ -1100,4 +1101,28 @@ bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { return success; } +bool PreReadImage(const wchar_t* file_path, size_t size_to_read, + size_t step_size) { + HMODULE dll_module = LoadLibraryExW( + file_path, + NULL, + LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES); + + if (!dll_module) + return false; + + PEImage pe_image(dll_module); + PIMAGE_NT_HEADERS nt_headers = pe_image.GetNTHeaders(); + size_t actual_size_to_read = size_to_read ? size_to_read : + nt_headers->OptionalHeader.SizeOfImage; + volatile uint8* touch = reinterpret_cast<uint8*>(dll_module); + size_t offset = 0; + while (offset < actual_size_to_read) { + uint8 unused = *(touch + offset); + offset += step_size; + } + FreeLibrary(dll_module); + return true; +} + } // namespace file_util diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc index e361db3..a47f3cb 100644 --- a/chrome/app/client_util.cc +++ b/chrome/app/client_util.cc @@ -5,6 +5,7 @@ #include <windows.h> #include <shlwapi.h> +#include "base/file_util.h" #include "chrome/app/breakpad_win.h" #include "chrome/app/client_util.h" #include "chrome/common/chrome_switches.h" @@ -110,54 +111,35 @@ HMODULE LoadChromeWithDirectory(std::wstring* dir) { // Experimental pre-reading optimization // The idea is to pre read significant portion of chrome.dll in advance // so that subsequent hard page faults are avoided. - DWORD pre_read_size_mb = 0; if (!cmd_line.HasSwitch(switches::kProcessType) && (IsRunningHeadless() || InstallUtil::IsChromeFrameProcess())) { + // The kernel brings in 8 pages for the code section at a time and 4 pages + // for other sections. We can skip over these pages to avoid a soft page + // fault which may not occur during code execution. However skipping 4K at + // a time still has better performance over 32K and 16K according to data. + // TODO(ananta) + // Investigate this and tune. + const size_t kStepSize = 4 * 1024; + + DWORD pre_read_size = 0; + DWORD pre_read_step_size = kStepSize; + DWORD pre_read = 1; + HKEY key = NULL; if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { - DWORD unused = sizeof(pre_read_size_mb); - if (::RegQueryValueEx(key, L"PreRead", NULL, NULL, - reinterpret_cast<LPBYTE>(&pre_read_size_mb), - &unused) != ERROR_SUCCESS) { - pre_read_size_mb = 16; // Default - } + DWORD unused = sizeof(pre_read_size); + RegQueryValueEx(key, L"PreReadSize", NULL, NULL, + reinterpret_cast<LPBYTE>(&pre_read_size), &unused); + RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL, + reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused); + RegQueryValueEx(key, L"PreRead", NULL, NULL, + reinterpret_cast<LPBYTE>(&pre_read), &unused); RegCloseKey(key); key = NULL; - } else { - pre_read_size_mb = 16; // Read in first 16 MB by default. } - } - - if (pre_read_size_mb) { - HANDLE chrome_dll = CreateFile(dir->c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL); - if (chrome_dll == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - DLOG(ERROR) << __FUNCTION__ << " CreateFile( " - << dir->c_str() << " ) failed. Error: " << error; - } else { - const size_t kChunkSize = 1024 * 1024; // 1 MB - void* buffer = VirtualAlloc(NULL, kChunkSize, MEM_COMMIT, PAGE_READWRITE); - if (buffer) { - size_t read_size = pre_read_size_mb * kChunkSize; - DWORD read = 0; - while (::ReadFile(chrome_dll, buffer, kChunkSize, &read, NULL)) { - // nothing to do here... - read_size -= std::min(size_t(read), read_size); - if (!read || !read_size) - break; - } - - VirtualFree(buffer, 0, MEM_RELEASE); - } - - CloseHandle(chrome_dll); + if (pre_read) { + file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size); } } diff --git a/chrome_frame/test/perf/chrome_frame_perftest.cc b/chrome_frame/test/perf/chrome_frame_perftest.cc index e22b143..614cb0d 100644 --- a/chrome_frame/test/perf/chrome_frame_perftest.cc +++ b/chrome_frame/test/perf/chrome_frame_perftest.cc @@ -12,6 +12,7 @@ #include "chrome_tab.h" // Generated from chrome_tab.idl. #include "base/file_path.h" +#include "base/file_util.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/registry.h" @@ -404,26 +405,42 @@ class ChromeFrameStartupTestActiveX : public ChromeFrameStartupTest { // This class measures the load time of chrome and chrome frame binaries class ChromeFrameBinariesLoadTest : public ChromeFrameStartupTestActiveX { + static const size_t kStepSize = 4 * 1024; + public: + ChromeFrameBinariesLoadTest() + : pre_read_(false), + step_size_(kStepSize), + bytes_to_read_(0) {} + protected: virtual void RunStartupTestImpl(TimeTicks* start_time, TimeTicks* end_time) { *start_time = TimeTicks::Now(); + if (pre_read_) { + EXPECT_TRUE(file_util::PreReadImage(chrome_exe_.value().c_str(), + bytes_to_read_, + step_size_)); + EXPECT_TRUE(file_util::PreReadImage(chrome_dll_.value().c_str(), + bytes_to_read_, + step_size_)); + } + HMODULE chrome_exe = LoadLibrary(chrome_exe_.value().c_str()); - ASSERT_TRUE(chrome_exe != NULL); + EXPECT_TRUE(chrome_exe != NULL); HMODULE chrome_dll = LoadLibrary(chrome_dll_.value().c_str()); - ASSERT_TRUE(chrome_dll != NULL); - - HMODULE chrome_tab_dll = LoadLibrary(chrome_frame_dll_.value().c_str()); - ASSERT_TRUE(chrome_tab_dll != NULL); + EXPECT_TRUE(chrome_dll != NULL); *end_time = TimeTicks::Now(); FreeLibrary(chrome_exe); FreeLibrary(chrome_dll); - FreeLibrary(chrome_tab_dll); } + + bool pre_read_; + size_t bytes_to_read_; + size_t step_size_; }; // This class provides functionality to run the startup performance test for @@ -929,21 +946,42 @@ TEST_F(ChromeFrameBinariesLoadTest, PerfWarm) { } TEST_F(ChromeFrameStartupTestActiveX, PerfCold) { + SetConfigInt(L"PreRead", 0); FilePath binaries_to_evict[] = { gears_dll_, avcodec52_dll_, avformat52_dll_, avutil50_dll_, chrome_exe_, chrome_dll_, chrome_frame_dll_}; RunStartupTest("cold", "t", "about:blank", true /* cold */, arraysize(binaries_to_evict), binaries_to_evict, false /* not important */, false); + DeleteConfigValue(L"PreRead"); +} + +TEST_F(ChromeFrameStartupTestActiveX, PerfColdPreRead) { + SetConfigInt(L"PreRead", 1); + FilePath binaries_to_evict[] = { gears_dll_, avcodec52_dll_, + avformat52_dll_, avutil50_dll_, chrome_exe_, chrome_dll_, + chrome_frame_dll_}; + RunStartupTest("cold_preread", "t", "about:blank", true /* cold */, + arraysize(binaries_to_evict), binaries_to_evict, + false /* not important */, false); + DeleteConfigValue(L"PreRead"); } TEST_F(ChromeFrameBinariesLoadTest, PerfCold) { - FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_, chrome_frame_dll_}; + FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; RunStartupTest("binary_load_cold", "t", "", true /* cold */, arraysize(binaries_to_evict), binaries_to_evict, false /* not important */, false); } +TEST_F(ChromeFrameBinariesLoadTest, PerfColdPreRead) { + FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; + pre_read_ = true; + RunStartupTest("binary_load_cold_preread", "t", "", true /* cold */, + arraysize(binaries_to_evict), binaries_to_evict, + false /* not important */, false); +} + TEST_F(ChromeFrameStartupTestActiveXReference, PerfWarm) { RunStartupTest("warm", "t_ref", "about:blank", false /* cold */, 0, NULL, true /* important */, false); |