What does multicore assembly language look like?

Viewed 62624

Once upon a time, to write x86 assembler, for example, you would have instructions stating "load the EDX register with the value 5", "increment the EDX" register, etc.

With modern CPUs that have 4 cores (or even more), at the machine code level does it just look like there are 4 separate CPUs (i.e. are there just 4 distinct "EDX" registers) ? If so, when you say "increment the EDX register", what determines which CPU's EDX register is incremented? Is there a "CPU context" or "thread" concept in x86 assembler now?

How does communication/synchronization between the cores work?

If you were writing an operating system, what mechanism is exposed via hardware to allow you to schedule execution on different cores? Is it some special priviledged instruction(s)?

If you were writing an optimizing compiler/bytecode VM for a multicore CPU, what would you need to know specifically about, say, x86 to make it generate code that runs efficiently across all the cores?

What changes have been made to x86 machine code to support multi-core functionality?

11 Answers

This isn't a direct answer to the question, but it's an answer to a question that appears in the comments. Essentially, the question is what support the hardware gives to multi-core operation, the ability to run multiple software threads at truly the same time, without software context-switching between them. (Sometimes called an SMP system).

Nicholas Flynt had it right, at least regarding x86. In a multi-core environment (Hyper-threading, multi-core or multi-processor), the Bootstrap core (usually hardware-thread (aka logical core) 0 in core 0 in processor 0) starts up fetching code from address 0xfffffff0. All the other cores (hardware threads) start up in a special sleep state called Wait-for-SIPI. As part of its initialization, the primary core sends a special inter-processor-interrupt (IPI) over the APIC called a SIPI (Startup IPI) to each core that is in WFS. The SIPI contains the address from which that core should start fetching code.

This mechanism allows each core to execute code from a different address. All that's needed is software support for each hardware core to set up its own tables and messaging queues.

The OS uses those to do the actual multi-threaded scheduling of software tasks. (A normal OS only has to bring up other cores once, at bootup, unless you're hot-plugging CPUs, e.g. in a virtual machine. This is separate from starting or migrating software threads onto those cores. Each core is running the kernel, which spends its time calling a sleep function to wait for an interrupt if there isn't anything else for it to be doing.)

As far as the actual assembly is concerned, as Nicholas wrote, there's no difference between the assemblies for a single threaded or multi threaded application. Each core has its own register set (execution context), so writing:

mov edx, 0

will only update EDX for the currently running thread. There's no way to modify EDX on another processor using a single assembly instruction. You need some sort of system call to ask the OS to tell another thread to run code that will update its own EDX.

As I understand it, each "core" is a complete processor, with its own register set. Basically, the BIOS starts you off with one core running, and then the operating system can "start" other cores by initializing them and pointing them at the code to run, etc.

Synchronization is done by the OS. Generally, each processor is running a different process for the OS, so the multi-threading functionality of the operating system is in charge of deciding which process gets to touch which memory, and what to do in the case of a memory collision.

If you were writing an optimizing compiler/bytecode VM for a multicore CPU, what would you need to know specifically about, say, x86 to make it generate code that runs efficiently across all the cores?

As someone who writes optimizing compiler/bytecode VMs I may be able to help you here.

You do not need to know anything specifically about x86 to make it generate code that runs efficiently across all the cores.

However, you may need to know about cmpxchg and friends in order to write code that runs correctly across all the cores. Multicore programming requires the use of synchronisation and communication between threads of execution.

You may need to know something about x86 to make it generate code that runs efficiently on x86 in general.

There are other things it would be useful for you to learn:

You should learn about the facilities the OS (Linux or Windows or OSX) provides to allow you to run multiple threads. You should learn about parallelization APIs such as OpenMP and Threading Building Blocks, or OSX 10.6 "Snow Leopard"'s forthcoming "Grand Central".

You should consider if your compiler should be auto-parallelising, or if the author of the applications compiled by your compiler needs to add special syntax or API calls into his program to take advantage of the multiple cores.

Each Core executes from a different memory area. Your operating system will point a core at your program and the core will execute your program. Your program will not be aware that there are more than one core or on which core it is executing.

There is also no additional instruction only available to the Operating System. These cores are identical to single core chips. Each Core runs a part of the Operating System that will handle communication to common memory areas used for information interchange to find the next memory area to execute.

This is a simplification but it gives you the basic idea of how it is done. More about multicores and multiprocessors on Embedded.com has lots of information about this topic ... This topic get complicated very quickly!

The assembly code will translate into machine code that will be executed on one core. If you want it to be multithreaded you will have to use operating system primitives to start this code on different processors several times or different pieces of code on different cores - each core will execute a separate thread. Each thread will only see one core it is currently executing on.

I think the questioner probably wants to make a program run faster by having multiple cores work on it in parallel. That's what I would want anyway but all the answers leave me no wiser. However, I think I get this: You can't synchronize different threads down to instruction execution time accuracy. So you can't get 4 cores to do a multiply on four different array elements in parallel to speed up processing by 4:1. Rather, you have to look at your program as comprising major blocks that execute sequentially like

  1. Do an FFT on some data
  2. Put the result in a matrix and find the Eigenvalues and eigenvectors of it
  3. Sort the latter by eigenvalue
  4. repeat from step one with new data

What you can do is run step 2 on the results of step 1 while running step one in a different core on new data, and running step 3 on the results of step2 in a different core while step 2 is running on the next data and step 1 is running on the data after that. You can do this in Compaq Visual Fortran and Intel Fortran which is an evolution of CVF by writing three separate programs/ subroutines for the three steps and instead of one "calling" the next it calls an API to start its thread. They can share data by using COMMON which will be COMMON data memory to all threads. You have to study the manual till your head hurts and experiment until you get it to work but I have succeeded once at least.

Related