21

A comment in another question about supervisor and user modes in the Motorola 68000 processors, claimed that "performance was abysmal" with virtual memory enabled. An InfoWorld article about System 7 corroborates this by saying,

To avoid slow performance, Apple suggests that the amount of virtual memory you select be less than the system RAM.

Was this poor performance caused by System 7, or by the CPU/(P)MMU, or by the applications themselves? Why did Microsoft Windows not experience poor performance with virtual memory turned on except when applications ran out of memory?

  • 1
    Wasn't it rather that System 7 was considered slow generally? Because of its much larger memory footprint than earlier versions? VM wouldn't have improved that situation, though. – tofro Apr 29 '17 at 9:56
  • 1
    Depends on the hardware, naturally. System 7 on a IIci or Quadra with 32M RAM is pretty 'snappy'. On a Plus with 4MB, not so much. Just like Windows, it ran better on the higher-spec'd machines of its time. – Brian H Apr 29 '17 at 15:00
  • 2
    RAM Doubler was a faster alternative to System 7's VM from Connectix. RAM Doubler used memory compression to try to avoid writing to disk which speed things up considerably. However Connectix broke more applications, extensions, and debugging in general to make their software work. – Michael Shopsin May 1 '17 at 17:32
16

There are several reasons for the low performance of virtual memory. The implementation had a significant effect.

It keeps ALL of the contents of memory in the VM Storage file, plus however much extra you've set it to, so all memory writes are also to disk also, even if not all reads are. Source

Program design also affected this. Many memory-hungry programs developed before virtual memory were designed to cope with low-memory computers in a similar way to System 7.

Some applications, like Photoshop, kind of did their own via temp files [...] In some cases you might have two writes contesting for resources. Source

The processor and MMU (or lack thereof) significantly affected virtual memory's performance.

Some portion of the main processor was dedicated to maintaining the paging file on the HD. [...] System 7 was designed to run on those 68k Motorola processors [...] The Intel 386 on the other hand had paging file management built in. Source

For the 68020 [the MMU] was a coprocessor that many machines like the MacII had sockets for. Source

Presumably to ensure compatibility with older hardware, System 7 didn't use DMA for disk access.

Disk I/O on most Macs was done by using the CPU to do transfers [...] Not using DMA meant that any transfer of data between disk and memory for paging required the CPU to do all the work.

Computers that use DMA [could] run programs while the transfer ran, so [...] Windows machines felt faster with virtual memory. Source

However, many performance with virtual memory were caused by incorrect configuration or use.

Defragment and optimize the hard drive. VM performance suffered greatly if this wasn't done. [...] If the OS and apps you needed to run required 4MB RAM, then you really should have at least 4MB RAM. [...] Set your VM page size to only 1MB more than your physical memory size.

You'd see things "perform poorly" [...] when you were switching from one memory-hungry app to another memory-hungry app (e.g., PageMaker to Adobe Illustrator) and that performance hit would only last as long as the process of switching between the apps. [...] You got used to the workflow of doing as much work as you could in a single app and only switching to another app when there was something that you absolutely had to get done there [...] If you lived on a diet of "low impact" programs, application switch time was tolerable if not speedy. Source


All of this information was sourced from /r/retrocomputing and /r/VintageApple. If you found this information useful, please give credit where it is due and upvote the original comments.

  • 1
    "It keeps ALL of the contents of memory in the VM Storage file...so all memory writes are also to disk also" I wonder who is responsible for this behavior, the OS or the MMU? On Wintel, memory is only written to disk when it needs to free up RAM. – snips-n-snails Apr 30 '17 at 15:37
  • @traal It could be a feature - quick hibernation times and data recovery after a crash. – wizzwizz4 Apr 30 '17 at 15:42
  • 1
    Writes to storage couldn't generate immediate writes to disk. If they did, that would reduce performance by a factor of more than 10,000. I think a bigger issue is that there was a fixed-sized set of addressable pages, each of which was mapped to a fixed location on disk, and the system didn't attempt to consolidate areas of memory that would be written and read, those which, having bitten written once, would only be read after that, and those which weren't used at all, so as to minimize the amount of effort needlessly writing information which would either... – supercat Oct 8 '18 at 16:08
  • ...match what was already on disk, or which wasn't needed for any purpose and could safely be abandoned. – supercat Oct 8 '18 at 16:09
12

I would say poor performance was due to System 7's implementation, and the constraints needed to support existing applications/drivers/extensions. Looking through the documentation, it's not hard to see why performance could suffer.

From Inside Macintosh: Memory:

The Virtual Memory Manager takes special steps to avoid double page faults caused by user code (that is, code that is not executed as the result of an exception). It defers all user code while the driver of the paging device is busy. In particular, the Virtual Memory Manager defers until a safe time the following types of code:

  • VBL tasks
  • Slot-based VBL tasks
  • Time Manager tasks
  • I/O completion routines

Note Because these types of tasks may be deferred under virtual memory, any application or device driver that uses them to achieve real-time performance might be adversely affected by the operation of the Virtual Memory Manager.

In other words, if the hard disk driver was busy, no user code could run. This would definitely add lag to callbacks that were previously rock-solid dependable.

Technote TN1094: Virtual Memory Application Compatibility is also interesting. The implementation was certainly improved on PPC Macs and later Mac OS versions,

  • 4
    Welcome to Retrocomputing! Thanks for posting this answer to an already-answered question; too many people are discouraged from doing this. – wizzwizz4 May 1 '17 at 8:42
  • Yes, thrashing creates a significant performance penalty in all operating systems, but I'm interested in why System 7 was worse than its contemporary, Windows 3.0. – snips-n-snails May 1 '17 at 18:05
  • 3
    I think the issue raised here isn't thrashing: it's that while paging is occurring, everybody else is locked out of any subsystem incidentally involved, whether they would have caused an additional fault or not. That's my understanding of this answer. – Tommy May 1 '17 at 23:21
  • Right, I wouldn't call it thrashing. Just that whenever the disk driver is busy (either handling a page fault or just regular I/O), no user code could run. And given how slow drives were, that would add real lag to what were previously very responsive applications. I don't know how this compares to Windows 3.0, maybe there were similar restrictions when it used DOS disk drivers? – Brendan Shanks May 2 '17 at 4:05
  • The link to Technote TN1094 leads to another technote that gives some clues about why System 7 pre-7.5.5 performed poorly with VM enabled... – snips-n-snails May 2 '17 at 15:49
4

with system 7, one had to have an '030 or better cpu (or an 020 with PMMU, which was not very common) - Mac II was most at risk here, and then, some did drop in a PMMU, but many jumped for the IIx upgrade instead (or IIfx later on).

The rule of thumb back in the day was VM was half of installed RAM with M68K.

It didn't perform "poorly" - it was fine enough - the challenge here, is that some apps, like Photoshop, did their own scheme, which could interfere/impact System 7 VM...

Connectix did some fun stuff with RamDoubler/Speed Doubler - and recall, they also did cool stuff like Mode32 (for legacy mac with 32-bit dirty ROM's) - and Virtual for System 6... with the IIsi, Optima which opened up addressable memory, and Maxima, which was a disk backed RAM disk.

Where things did get a bit odd - the PPC migration - and legacy M68K code vs. native PPC code - but once the PCI Macs showed up, that was less of an issue (painful though the transition was) and later versions of Classic...

Old memories...

And someone had mentioned about disk-thrashing...

HD-SC Setup, and the Apple drivers back in the day, if I recall correctly, where sync - and blocking on I/O (and HD-SC Setup only supported Apple ROM'd drive)

FWB Hard Disk Toolkit - 3rd party - had Async I/O - and non-blocking at that - so folks working creative production, that was the hot item back in the late M68K days, as they also supported generic SCSI drives...

  • Welcome to Retrocomputing Stack Exchange. I recommend that you read the tour - Stack Exchange sites are different from the standard "forum". Do you know why the rule of thumb was additional VM = half RAM, and not some other number? – wizzwizz4 May 2 '17 at 6:36
  • 1
    If I recall correctly - no formal guidance from Apple, but between experts in the field, press, and on the USENET that was -- I suppose it did matter how much RAM installed in the first place - if one had 32-64MB, then setting VM to +1MB, just to take advantage of file mapping (post 7.6) - so always chances to optimize based on which CPU, how much RAM, portable or not, and what the general usage model was. – sfx2k May 2 '17 at 22:06
2

Some comments on virtual memory and paging in general:

Virtual memory is a means by which it can be made to appear that the computer has more memory than it actually does. The way it is implemented is with a page table. The memory is divided up into fixed sized pages (on 32 bit microprocessors, pages were always 4096 bytes in size). Memory addresses were divided into two parts: page number being the top 20 bits (in a 32 bit address architecture with a 4kbyte page size) and the address in the page being the lower 12 bits.

The processor maintains a page table indexed by virtual page number that contains the physical page number and some other information such as

  • is the page dirty i.e. has it been written to since it was last flushed to disk,
  • is it valid i.e. is the page actually loaded into physical RAM at all
  • some protection information e.g. is the CPU allowed to write to the page (or read from it, for that matter).

Every time the processor wants to access the RAM, it first has to go to the page table (which is itself in RAM) to find the physical page for the virtual page in the address. Thus you double the number of memory accesses required just to read a word from memory.

There's the first performance hit. By turning on virtual memory, you double the number of RAM accesses required. Not only that, if your memory management unit was in a separate coprocessor (as in the 68020) there is a performance hit just for the communication between the two.

In mainframe and minicomputers, this performance hit was mitigated with fancy hardware which did sophisticated caching - not so much on the 80386 and 68020 and 68030.

A second problem: you needed a paging table. If you wanted to allow processes to map the full 4Gb of virtual memory, your paging table needed 220 entries of 32 bits each. That is 4Mb. If your computer has 4Mb then all the physical RAM is being used just for the page table. The problem of the page table eating RAM is the main reason why Microsoft's recommendation to keep the paging file at no more than twice the size of the physical RAM and the similar restriction for System 7 was probably for the same reason.

Note also that, if you want each process to have the illusion of having a unique address space, you actually need a page table per process and the page table needs to be swapped on each context switch.

Now let's talk about accessing pages. When the CPU wants to look at the content of a virtual address, assuming it is not cached, it looks up the virtual page in the page table and if the page table says the virtual page is mapped to a physical page, a new address is constructed of the physical page and the offset within the page and the data is fetched. If the virtual page is not mapped to a physical page, then depending on wht kind of data is stored in the virtual page, one of several things happen.

Firstly, the page is not there so the process cannot continue. A trap called a page fault is generated and the OS suspends the process, then it tries to fix the problem by allocating a new physical page and:

  • if the page comes from the running program, the physical page is loaded from the program's executable file on disk.
  • if the page is newly allocated data e,g, from a malloc request, the physical page is filled with zeroes
  • if the page has previously been swapped out, the physical page is loaded from the paging file.

In all of these cases there is a performance hit: the process has to wait for either a page to be zeroed out or 4kb of data to be loaded from disk. Obviously, in the latter case, if either the paging file or the program is fragmented, it's going to take longer.

What if there aren't any physical pages free to allocate to the process? In this case, a page needs to be thrown away. But which one to choose? The optimal method is to throw away a page you are not going to use again or to throw away a page that you are not going to need for a very long time. Unfortunately this requires clairvoyance so expensive mainframes at the time of the 80386 and 68030 used an algorithm called LRU which stands for "least recently used" which gives quite a good approximation to the clairvoyant algorithm. In LRU you throw away the page that has not been used for the longest time. This requires expensive hardware support, namely a time stamp for each physical page which is updated every time it is used. Microprocessors had to use more complicated and less satisfactory algorithms because they didn't have the time stamp support.

Some of the answers to this question suggest that System 7 always wrote pages to the paging file when they were dirty (i.e. the data in them was modified). This is important because pages that are not dirty are much cheaper to throw out because you don't have to write them to the page file first. However, that makes the assumption that all pages will be thrown out at some point. If you have loads of RAM you incur the hit of writing dirty pages to disk even if no pages are ever going to need to be swapped out.

So, in summary, virtual memory is a performance hit because

  • each memory access actually needs two memory accesses
  • the page table (or tables) itself uses up RAM
  • if a virtual page is not allocated a physical page, you have to wait for the OS to allocate one
  • retrieving pages from disk and writing them to disk takes a long time

We still use virtual memory because

  • each process can appear to have the entire address space to itself which greatly simplifies programming
  • paging eliminates RAM fragmentation from the point of view of the operating system
  • you need less RAM than the sum of the memory requirements of all running processes.

Edit

With respect to the page table and CPU cache that traal mentioned, CPU cache does indeed mitigate the problem of multiple memory accesses somewhat but the 68030 only had a 256 byte cache.

Furthermore, my description of page tables was slightlyheavily simplified: both the 386 and the 68030 had two level page tables. On the 386 for example, there was first a look up in the page directory which was a 4k block containing pointers to (up to) 1024 page tables, each of which was a 4k table containing 1024 page entries. Thus (ignoring any caching), referencing a memory address required three accesses to memory, not two as previously stated. The 68030 used a similar but slightly more complex scheme, you could vary the size of the individual pages.

  • "each memory access actually needs two memory accesses" That's a good point, but the CPU cache should hide that issue. So why did System 7 on a 68020+MMU/68030 suffer more than Windows 3.0 on an 80386 when virtual memory was enabled? – snips-n-snails May 2 '17 at 15:10
  • @traal the CPU cache certainly does mitigate the issue to an extent but bear in mind that a 68030 had 256 bytes of cache. That's not enough to offset the cost of page table translation. – JeremyP May 2 '17 at 15:22
  • Not sure why Win 3.0 beat system 7 in VM mode but, if as people above suggest, System 7 wrote all page updates to disk instead of waiting until they were about to be paged out, that would be a big hit on performance. Also, I'm not sure if Windows 3.0 exploited the full capabilities of 386 protected mode. Programs running in Win 3.0 were still essentially 16 bit. – JeremyP May 2 '17 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.