Skip to main content
gamedev.net
🔒 Locked

SLI vs. Multiple threads

Started by Ezbez May 25, 2006 at 6:55 PM 4 replies 800+ views
Original Post
Ezbez
Ezbez
To start this, I would like to say that I have never programmed a multithreaded program before, and have never used an SLI computer. All that I say here is based on what I have heard other posters or websites say. To summarize, I am a newb on these topics. Also, everything here is a generalization. The specifics will change many things, obviously. I have heard that SLI splits the screen into two and passes one half to one card, and the other to the other card. This is definately a very simplistic view of what is going on, but it is enough(I hope) for this post. SLI seems to be very very effective at increasing performance. nVidia advertises an increase of 80-90% FPS for some cards. Not only that, but it requires no work on the programmer's part (apart from some optimizations that can be made). No redesigning the entire program to make it work for SLI. From what I've heard, multithreaded programs almost always give one core one task and another core another task. So, as an example, one thread ends up with audio, another AI, and another for physics. Then they are all integrated back together. This seems fundamentally different from the approach of SLI. So, here comes the main point of this post. Why isn't SLI handled like multithreaded programs and why aren't multithreaded programs handled like SLI? As I see it, if multithreaded programs were to take the SLI approach, and have, say, one thread take the first half of the physics, and the second thread take the second half, wouldn't you be able to take away much of the downtime waiting for other threads to finish or communicating between threads? As I see it, audio of one frame does not need to communicate with audio of the same frame, physics does not need to communicate with other physics during the same frame, and AI does not need to communicate with other AI during that same frame. Thus, an SLI-like approach *may* be faster or easier to program. And if an SLI-like approach isn't going to be faster, why does nVidia use it? If any of this information is incorrect, please tell me. And if there are some programs that use a multithreading settup that is similiar to how SLI works, please tell me.
janta
janta
Quote:
Original post by Ezbez
why aren't multithreaded programs handled like SLI?

Hello!
I dont know much about this, but I just wanted to mention openMP (which stands for Multi Processing I think, or sth like that) In enables you to parallelize (is that an english word ? ^^) certain tasks.

For example a for loop with 1000 iteration can, ** on certain conditions ** be split into 2 sub loops which are processed simultaneously on two cores for better effectiveness. OpenMP allows you that by using some #pragmas before and after the loop. So I think this is pretty similar to SLI isn't it ?

KulSeran
KulSeran
I would assume it(no SLI thread approach) stems from most people programming with somewhat baseline code that was layed down by someone else.
So, assume you are working with ODE, SDL, and FMOD. You aren't going to want to break into the ODE lib to make it multi-threaded.
You aren't going to touch SDL or FMOD either. So, you are stuck trying to pull performance out of a system of libs.
One way to deal with this is the one-task-per-core approach.
You could, in theory spit the data processing across cores, but then you have to deal with the fact that most
libs can modify data in-place, keep random buffers, and finish at an undetermined time.
So, on the off chance that the previous frame data is needed (for collisions or something)
then you need the old state data, and most libs don't have a concept of "then" and "now", since they assume stuff about your data.

Now, making your own code, there is no reason not to have worker threads, that each take 1/nth the data to split over n cores.
But, there needs to be enough data to make sure that all the threads are busy.
And if each thread needs some global data (like the map), then you have to think that you just lost cache locality of the data.
Where on one thread, you can sort your data to make sure you process everything in one map sector before moving to the next,
in more threads you can't control the memory access caused by processing the same data, and can actually lose speed
from the less controled memory access patern.
(There are benchmarks for some webserver software that show hyperthreaded cores giving a drop in performance for this exact reason.
Even though the code was sound, the archatecture caused slowdowns to happen that need to be considered)

Mentioned above, OpenMP is really good at vectorizing your tight inner loops. It can split a large loop onto several cores
if there is a defined access pattern that indicates that you don't need to complete the first part of the loop to do the
second part.
It appears that Oblivion use this to help speed up the game code.
Ezbez
Ezbez
Thanks both of you, especially KulSeran. I think I get it now.

Though with ODE, you could have one space(forget the exact term) on one thread, and another on another thread. But, yes, SDL, FMOD, and most other libraries would be difficult to use multithreaded.

Thanks!
The Reindeer Effect
The Reindeer Effect
SLI can multitask so easily because the problem is data-parallel. Not all software is data-parallel: consider the factorial function,

int fact(int n) { // recursive  if(n == 0)     return 1;  return n * fact(n-1); }int fact(int n) { // iterative  int c;  for(c = 1; n > 0;  n--) {    c *= n;  }  return c;}


In the recursive implementation, there is no data parallelism whatsoever. To compute fact(5) we must know the value of fact(4) and so on. And because this function is linearly recursive, there is nothing we can do but follow down the trail of computations. There is a data hazard preventing us from the free parallelization, and we can only resort to fine-grained control parallelism (compilers may take advantage of this, and most modern processors do ... superscalar, pipelines, etc).

The iterative implementation is a similar situation, except that advanced vectorizing compilers can actually deconstruct this into superscalar (SIMD, AltiVec) instructions. Keep in mind there are really two sorts of vectoring: the vectorization of sequential code into parallel code to be executed by multiprocessors, and the vectorization of sequential code into superscalar code to be executed by a single processor. High Performance Fortran is an example of the former, VectorC is an example of the latter. Neither techniques are trivial, and they flat out can't provide the sort of performance improvement that a data parallel application could achieve. And the particular example I gave was a very easy one to parallelize (in the scheme of things). Imagine if I had used division instead of multiplcation: now we have lost commutativity and associativity.

And there *still* is no way to parallelize the iterative implementation without actually altering the algorithm we use to compute factorials. And I don't just mean throwing threads into the fray ... I mean the essence of how we compute factorials would have to be altered.

So, some algorithms are more amenable to parallelization than others. But this doesn't mean that throwing N processors at a problem will improve it's performance by N. In many cases, the performance improvement is not even within an epsilon of N!

[Edited by - The Reindeer Effect on May 27, 2006 12:24:58 AM]
ajas95
ajas95
Ezbez,
It is an interesting question... generally, how to find some division in your problem so that 1/N of the problem can be computed on each of N processors. For SLI, 95% of the work is processing pixels so it makes sense to divide the pixel work evenly. However, it only works because the processing of each pixel is independent of each other pixel (pixels in a triangle will share interpolants from the vertices, but no pixel can be affected by adjoining pixels without rendering first to a texture, for example).

And so the goal boils down to selecting problems that can be computed in parallel, which have no inter-dependencies. The programmer is the only one who knows how to formulate this. Many, many things cannot be computed in parallel... or else they have dependencies that are not easily computed in parallel.

Here's an example. You would think that all particle systems could be evolved in parallel on separate processors. This is generally true. Unfortunately, it is frequently the case that particles are being emitted based on some attachment to a bone in an animating character. So, the particles being evaluated depends on particles being emitted, which depends on the character having its animations blended together... which depends on bones being evaluated... which depends on game logic having chosen the currently playing animations.

So really, the goal is to design a game so that an entire frame can be processed in N chunks that are not inter-dependent. This is very hard because a game loop usually involves a number of steps, each of which depends on the completion of the previous (game logic->animation->collision->response->particles->rendering).

I would say: Embrace this complexity! It is real.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.