|
|
Title | Use Microsoft's .NET Framework parallel extensions to generate Mandelbrot sets quickly in Visual Basic 2008 |
Description | This example shows how to use Microsoft's .NET Framework parallel extensions to generate Mandelbrot sets quickly in Visual Basic 2008. |
Keywords | parallel extensions, parallel programming, multiple CPUs, multi-core, Mandelbrot, Mandelbrot set, .NET, .NET Framework |
Categories | Windows, Algorithms, Graphics, VB.NET |
|
|
Microsoft's Parallel Extensions to .NET Framework allows you to more easily use the multiple CPUs that may be inside your computer. The overhead is low so you don't pay a big performance penalty if you have a single CPU.
As the speed of chips has started to level off, modern computers are including more and more CPUs in a single system. The new library makes it easier to take advantage of the extra power sitting idle in your computer. For example, I have a dual-core laptop but thue second core mostly sits around idle. This example generates Mandelbrot sets in less than half the time when it uses the parallel extensions library.
Here's the link to download Microsoft Parallel Extensions to .NET Framework 3.5, June 2008 Community Technology Preview.
After you download and install the library, make a reference to System.Threading. You may also want to add the statement Imports System.Threading to make using the library easier. It provides methods that let you (relatively) easily run code on multiple threads that may run on separate CPUs.
There are two main differences between this version and a normal Mandelbrot set generator. First, this version uses a subroutine named DrawXLine to draw a single vertical strip through the Mandelbrot set. It takes as a parameter an X coordinate and calcualtes the image's pixels for the pixels with that coordinate.
Eventually the program will need to set the pixels in a Bitmap object. Unfortunately if you try to do that in multiple threads, you get "Object in use by another process" type errors.
To avoid that, the routine stores the numbers used to determine eachv pixel's color in the array m_BmColors. The main program then uses those values to set the pixels after the separate threads finish.
|
|
Private Sub DrawXLine(ByVal X As Integer)
' Work until the magnitude squared > 4.
Const MAX_MAG_SQUARED As Integer = 4
Dim ReaC As Double = m_Xmin + X * dReaC
Dim ImaC As Double = m_Ymin
For Y = 0 To hgt - 1
Dim ReaZ As Double = Zr
Dim ImaZ As Double = Zim
Dim ReaZ2 As Double = Z2r
Dim ImaZ2 As Double = Z2im
Dim clr As Integer = 1
Do While clr < MaxIterations And ReaZ2 + ImaZ2 < _
MAX_MAG_SQUARED
' Calculate Z(clr).
ReaZ2 = ReaZ * ReaZ
ImaZ2 = ImaZ * ImaZ
ImaZ = 2 * ImaZ * ReaZ + ImaC
ReaZ = ReaZ2 - ImaZ2 + ReaC
clr = clr + 1
Loop
' Set the pixel's value.
m_BmColors(X, Y) = clr
ImaC = ImaC + dImaC
Next Y
ReaC = ReaC + dReaC
End Sub
|
|
The following code shows how invokes DrawXLine. If the variable m_Parallel is True, tghe program calls Parallel.For(0, wid, AddressOf DrawXLine). This makes the parallel library call DrawXLine with an integer parameter ranging from 0 to wid - 1. (Important: Notice that the upper bound passed in the second parameter is exclusive so the loop runs from 0 to wid - 1 not from 0 to wid. This is a C++/C# inspired loop counting technique.)
If m_Parallel is False, then the code uses a normal For loop to call DrawXLine itself. That makes the calls all run in the main user interface thread so you don't get the benefit of any extra CPUs lying around.
|
|
' dReaC is the change in the real part
' (X value) for C. dImaC is the change in the
' imaginary part (Y value).
wid = picCanvas.ClientRectangle.Width
hgt = picCanvas.ClientRectangle.Height
dReaC = (m_Xmax - m_Xmin) / (wid - 1)
dImaC = (m_Ymax - m_Ymin) / (hgt - 1)
' Calculate the values.
Dim start_time As Date = Now
If m_Parallel Then
Parallel.For(0, wid, AddressOf DrawXLine)
Else
For x As Integer = 0 To wid - 1
DrawXLine(x)
Next x
End If
Dim stop_time As Date = Now
' Copy the values into the bitmap.
For x As Integer = 0 To wid - 1
For y As Integer = 0 To hgt - 1
m_Bm.SetPixel(x, y, m_Colors(m_BmColors(x, y) Mod _
NumColors))
Next y
Next x
picCanvas.Invalidate()
|
|
There are still a lot of issues to address. A few are:
Two threads should not try to modify the same variables or they may cause race conditions (where the exact order of execution in the threads can change the result.
Many classes are not safe for multi-threading so two threads should not use the same object in many cases. In this example, the Bitmap object is not safe for multi-threading so the code must be rewritten so DrawXLine doesn't try to use the Bitmap directly.
If multiple threads try to use locks to access the same resources safely, they may cause a deadlock.
Only the user-interface thread can interact safely with the program's controls, forms, and other user interface elements. (I've had bad luck using Invoke but BeginInvoke seems to work.)
You can address these issues but they can be tricky. If you keep the routine called by Parallel.For nice and self-contained so different calls do not try to write to the same variables or use the results of other threads, then things work pretty well and you can tap the hidden power lying dormant in your computer.
I'll try to post more examples soon. Meanwhile here are some useful links:
Download: Microsoft Parallel Extensions to .NET Framework 3.5, June 2008 Community Technology Preview.
Parallel Performance: Optimize Managed Code For Multi-Core Machines. This is a good introduction to the parallel library.
Concurrency: What Every Dev Must Know About Multithreaded Apps.
Parallel LINQ: Running Queries On Multi-Core Processors.
|
|
|
|
|
|