What are the different repositories available / used by the program at runtime?

Typically, when you run some program at runtime, what are the available repositories available and what are they used for? I understand stack and heap. Also, I know that value types go on the stack and reference types go on the heap. But I also came across terms like program counter, instruction pointer.

What do they mean?

BOUNTY: Below are some really good answers. I'm looking for something more detailed. Something that doesn't make me read a few chapters from the COA book. Specific blogs / videos / explanation simplified.

+2


source to share


4 answers


The main memory types in any high-level language are the ones you have already defined: stack and heap.

There is no limit to what you can put in this or that; the difference is how and when this memory is allocated. Stack space is allocated when the function is called and immediately, so you cannot allocate new stack space from inside the function, and as soon as the function returns that memory, it is freed. On the other hand, heap space you allocate whenever you want, and in any amount you want.



There are, of course, other chunks of memory that are used internally that you usually don't touch. For example, "program counter" and "instruction pointer" are the same thing: one word of memory that keeps track of which instruction in your program is next to execute. Each time the CPU executes an instruction, the program counter advances to the next.

As soon as you make a function call, the program counter is stored on the stack along with your local variables, so when the called function returns to the calling function, it will know where it "left off".

+3


source


I think you are asking two different but related questions. First, how is the data for my program organized? Different architectures do it in different ways, but basically a program consists of 3 (or 4 depending on how you count them): data (heaps and static global data), stack (local and function calls / return data) and text (code).

Second, how does the computer run my program? It really is a matter of operating system semantics. the program counter or instruction pointer is usually one of the many registers on the CPU that are used when your program starts up. It keeps track of the memory location of the currently executing command. Others include a stack (or frame) pointer (the current location of the stack frame of executable functions), a program status word (information about the results of the current instruction), and data registers.



There is a lot more to it than running your program, and I've only skimmed the surface of the low-level hardware bits, but the questions you raise really do require a tutorial to be fully understood. For a more thorough treatment, I suggest collecting a copy of the Computer Organization and Architecture from your local bookstore or library .

+3


source


A program pointer and an instruction pointer are the same thing. Basically, an instruction pointer keeps track of what instruction the processor is executing. Your program will reside in a separate piece of memory, sometimes called a code segment. The instruction pointer points to a location in this segment.

It should be noted that the program counter is not stored in RAM (that would be too slow). This is a processor register. Only when a function call is made is the program counter stored on the stack. Then, when the program returns from the function call, the return value from the stack is returned and the program counter is restored.

See here for details .

+2


source


There are many different conceptual data stores in a computing machine, as well as many different actual memory stores for different specific implementations of the computing machine.

In the beginning there was a tape and a lot of it.

As an example, there is a conceptual Turing Machine , where the storage is TAPE, TABLE and STATE REGISTER.

These are all different forms of storage:

TAPE is changeable and unlimited. TABLE can be volatile (although often it isn't), but the constrained STATE register is volatile and constrained. HEAD POSITION is simple, but very important in that it allows you to move freely, limited only by the ribbon itself and the logic in the TABLE.

Further you can see that in the idealized models TABLE and STATE REGISTER are considered "fast" to access any record inside (it takes some constant time to access / change), while access to some arbitrary element of the tape depending on the distance from the current location on tape wherever the data is.

This allows significant analysis of various aspects of the computing system and critically simulates the idea that an unlimited data source in a system will inevitably exhibit non-linear behavior in terms of access cost (since the idea of ​​how to get data will itself grow as the stored data grows ).

Turing's memory model was not so far removed from real physical machines in the early days of mainframe computers, with most persistent storage based on (admittedly finite) tapes, and PST based on a small, simple pool of "memory" in a machine of equal cost to access any entry. Many of the earliest computers did not have "programs" in the sense that you or I think they are. Instead, they could change their TABLE (often literally by hand) and then all further behavior was corrected. This was the separation of DATA from INSTRUCTIONS.

As modern computers changed this relationship with one of the first formal descriptions of a computing device, it became increasingly distant.

Accidental is not considered dangerous

Most modern machines are based on the von Neumann Architecture . They explicitly place instructions and data in the same "pool" of address memory. Their initial TABLE is just enough to start reading instructions from memory, which can determine from the data which instructions to use in the future.

Many different ways have been designed to maintain state, some have stored it in the main memory pool, others have created specific areas of memory as arbitrarily addressable named "registers" separated from main memory, or as "stacks" by taking a "piece of main memory" ...

They are not exclusive, many have the concept of a stack and registers, some of which provide separate memory for the stack, while others do not put it in main memory. Several machines that are instructed (or encouraged through convention) that certain registers refer to the conceptual stack, a common name for a particular register in many architectures is the stack pointer (SP).

Almost everyone had the concept of a specific storage location (usually a register) that indicated the next instruction to execute. Changing this value (from base to add one to the last value), providing a basic facility for a general-purpose computer, so the instruction pointer (or IP, as this special place became known) is something like the location of HEAD on a Turing machine (except that it represents instructions, not data). By moving all data into a randomly addressable form, the concept of data tape was removed from "memory".

Instead of what was once the primary memory of a computer, it was instead assigned to that of an I / O device. Storage, but no more than what most people would call memory in the sense in which we use it now.

In early computers, this kind of registers and RAM usually ran at the same speed, although due to the cost of moving data from RAM to register and back, this was a simple instruction like any other.

As the speed at which the logical devices could operate increased, the speed at which it was possible to access (more and more) RAM was relative in relative terms. Part of that was that it quickly became easier to separate logical units from memory blocks in terms of cost.

Since such registers were "special" storage that was "close" to logical blocks, they usually ran at the same speed as logical ones. The main memory became "further" from the gates and cost more cycles to access.

Messages in a narrow space

The code concluded that it should be implemented in such a way as to reduce the number of copies of data that had to be copied to the register to be managed and then copied back to main memory. Computer designers realized that, in general, a distinctive "style" was actually quite common in many programs: a smaller portion of the main memory was available much more frequently than other portions.
The ability of the compiler / human to efficiently use registers to manage this is limited. Trying to organize computation to make efficient use of this faster storage involved complex dependency definitions. Adding registers, since each was a "special name", was not an easy solution, as it added more aggressive transcoding to each application and forced rewriting for each update.

It was realized that it was possible to place a portion of main memory "closer" to logic, perhaps not as close as registers, but much closer than to main memory. Since in many cases it was not worth saving (and perhaps was not) to close this memory, it must be a "window" into main memory. Early designers realized that putting this under the control of a programmer would lead to many of the same problems with a fixed number of registers. So they tried to use this closer memory in a way that is transparent to the programmer and program, except that it had a performance impact.

Cache memory was introduced, so temporary locality was made for faster computers without code changes.

Various forms have been tried (and many coexist even now), both exclusive and inclusive (now something can be in two places at the same time). It has also been calculated that if memory tends to have temporal locality, it also tends to be spatial locality, since such data can be in the cache even if you haven't requested it speculatively yet.

Some have split the cache between data and instructions, others not all, but almost all computers have moved to the cache.

It alternates all the way down

Not long before memory got so much slower than cache, it became worth nesting a slower but larger cache between main memory and small cache. Created level 1 and level 2 cache and even more.

More is better faster faster

As we got faster, it became clear that many of the codes were actually capable of executing concurrently, either through pipelining or independent parallel execution. At the same time, using several logical blocks "hidden" from a simple presentation, as was noticed by the programmer and the compiler, more "hidden" registers were required to account for multiple operations in flight. The rename registration has been designed so that multiple copies of the same "register" used in one of the different execution units can be used to hide changes in one of the other.
The "where" part of the government repository has become even more complex, although thankfully still hidden from most programmers.

At the same time, this was happening, and we started to experience problems related to the acceleration of one processor, mainly down to the bottleneck cache itself, which was trying to mitigate. As such a transistor budget began to be spent on other completely independent sections of logic, it was hoped that programmers would start writing code that expects multiple units, rather than one.

This leads to some problems. Previously, all "hidden" ways to mitigate memory latency never changed the programmer. Now, however, the "shared main memory" model of this new SMP world order was working against you. " the address "in a memory pair will be in many places at the same time, indeed, it can have several different values ​​in these places.

Now it became important not only where the memory was, but how stable it was. whether some of the memory was in the cache or not, and the programming models had to change. At this point, the Memory Model architecture has become very important (and very complex).

Divide and conquer

Since this complex interaction of "hidden" opaque memory manipulation did not interact well with the new model with several separate units, various possible solutions began to emerge.

One thing is clear if caching is not required in a value request to prevent this from happening, simplifying cache negotiation protocols.

Another less obvious one (and it is still unclear how much it fits) stops having this large shared memory pool, and instead gives each module its own chunk of memory, with an explicit interaction between them and the old main memory.

This division of memory becomes even more challenging as devices not previously seen in the same class as general-purpose CPUs bent into this model .

Don't forget about the future

This is of course only the beginning of new changes, in the future you might find more complex memory than qbits or something else unknown.

+2


source







All Articles