Re: Bochs icache perf when writing memory
"Stanislav" <[email protected]> Sat, 3 Oct 2020 11:37:53 +0300
| Newsgroups | gmane.comp.emulators.bochs.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, I applied your patch with small modifications and saw nice speedup over my WinXP boot benchmark. Some comments to your patch (might be you could improve it following the comments): v2h_write_byte is necessary to invalidate the whole page in ICache because it is used in fast string operations (REP MOVS, REP STOS and other similar x86 instructions). For example see faststring.cc: Bit32u BX_CPU_C::FastRepSTOSD(bx_address laddrDst, Bit32u val, Bit32u count) The function "saves" calling decWriteStamp on every copied 4-byte block and instead invalidates entire page first. Assuming that if you are doing memset/memcpy on same page, you probably should not have code in the same 4K page. It is certainly correct for modern OSes but might be not so correct for old stuff like WinXP. I don't know what is better -> calling decWriteStamp many times precisely or once but with 4K granularity. Can you advice ? Thanks, Stanislav -----Original Message----- From: Lander Brandt <[email protected]> Sent: Sunday, 2 August 2020 5:01 To: [email protected] Subject: [Bochs-developers] Bochs icache perf when writing memory Hello, I was recently doing some profiling of Bochs and noticed a lot of time in my build spent in the instruction decoder caused by icache misses. After reading the code intimately for a few days I *think* I have a pretty decent understanding of how it works and may have fixed some performance issues, but I would like to present my changes and see if what I changed is "legal". In memory.cc I noticed that I was hitting the memcpy-type case in the BX_MEM_C::{read,write}PhysicalPage methods and in every case I documented it was a read/write of 16 bytes. For writes this seemed particularly bad because it would call `pageWriteStampTable.decWriteStamp(a20addr);`, which if my understanding of the icache is correct may cause an entire page to be flushed from the icache for even small writes. I "fixed" this (it still has unnecessary work when the length is not a multiple of 8) and a similar condition in `BX_CPU_C::v2h_write_byte`. Anecdotally I noticed a very good performance increase by just eyeballing the IPS counter in the bottom-left of the GUI and the number of calls to `bxICache_c::handleSMC` with an 0xFFFF_FFFF mask dropped from 16,125,122 for ~1BN instructions to just 34. Again if my understanding of the icache is correct (and please explain it to me if my understanding is fundamentally broken), this resulted in a substantially fewer whole pages being flushed from the icache if they were previously mapped in. I also did not observe any *visible* negative impacts of this patch and my snapshot appeared to operate normally. Thanks! Here is my patch: diff --git a/bochs_src/cpu/access.cc b/bochs_src/cpu/access.cc index da1148ad..4a72f5ce 100644 --- a/bochs_src/cpu/access.cc +++ b/bochs_src/cpu/access.cc @@ -513,7 +513,7 @@ BX_CPU_C::v2h_write_byte(bx_address laddr, bx_bool user) bx_hostpageaddr_t hostPageAddr = tlbEntry->hostPageAddr; Bit32u pageOffset = PAGE_OFFSET(laddr); Bit8u *hostAddr = (Bit8u*) (hostPageAddr | pageOffset); - pageWriteStampTable.decWriteStamp(tlbEntry->ppf); + pageWriteStampTable.decWriteStamp(tlbEntry->ppf, 1); return hostAddr; } } diff --git a/bochs_src/memory/memory.cc b/bochs_src/memory/memory.cc index e896f1d1..d3fa737c 100644 --- a/bochs_src/memory/memory.cc +++ b/bochs_src/memory/memory.cc @@ -110,8 +110,6 @@ mem_write: // len == other, just fall thru to special cases handling } - pageWriteStampTable.decWriteStamp(a20addr); - #ifdef BX_LITTLE_ENDIAN data_ptr = (Bit8u *) data; #else // BX_BIG_ENDIAN @@ -122,18 +120,38 @@ mem_write: { // addr *not* in range 000A0000 .. 000FFFFF while(1) { - *(BX_MEM_THIS get_vector(a20addr)) = *data_ptr; - if (len == 1) return; - len--; - a20addr++; -#ifdef BX_LITTLE_ENDIAN - data_ptr++; -#else // BX_BIG_ENDIAN - data_ptr--; -#endif + // Read in chunks of 8 bytes if we can + if (len % 8 == 0) { + pageWriteStampTable.decWriteStamp(a20addr, 8); + WriteHostQWordToLittleEndian(BX_MEM_THIS get_vector(a20addr), *(Bit64u*)data_ptr); + len -= 8; + a20addr += 8; + #ifdef BX_LITTLE_ENDIAN + data_ptr += 8; + #else + data_ptr -= 8; + #endif + + if (len == 0) return; + } else { + pageWriteStampTable.decWriteStamp(a20addr, 1); + *(BX_MEM_THIS get_vector(a20addr)) = *data_ptr; + if (len == 1) return; + len--; + a20addr++; + #ifdef BX_LITTLE_ENDIAN + data_ptr++; + #else // BX_BIG_ENDIAN + data_ptr--; + #endif + } } } + + printf("LENGTH NOT HANDLED!!! %d\n", len); + pageWriteStampTable.decWriteStamp(a20addr); + // addr must be in range 000A0000 .. 000FFFFF for(unsigned i=0; i<len; i++) { @@ -273,15 +291,29 @@ mem_read: { // addr *not* in range 000A0000 .. 000FFFFF while(1) { - *data_ptr = *(BX_MEM_THIS get_vector(a20addr)); - if (len == 1) return; - len--; - a20addr++; -#ifdef BX_LITTLE_ENDIAN - data_ptr++; -#else // BX_BIG_ENDIAN - data_ptr--; -#endif + // Read in chunks of 8 bytes if we can + if (len % 8 == 0) { + ReadHostQWordFromLittleEndian(BX_MEM_THIS get_vector(a20addr), *(Bit64u*)data_ptr); + len -= 8; + a20addr += 8; + #ifdef BX_LITTLE_ENDIAN + data_ptr += 8; + #else + data_ptr -= 8; + #endif + + if (len == 0) return; + } else { + *data_ptr = *(BX_MEM_THIS get_vector(a20addr)); + if (len == 1) return; + len--; + a20addr++; + #ifdef BX_LITTLE_ENDIAN + data_ptr++; + #else // BX_BIG_ENDIAN + data_ptr--; + #endif + } } } _______________________________________________ bochs-developers mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/bochs-developers