为什么我的程序在单核上运行得更快?
为什么我的程序在单核上运行得更快?
我正在使用TPL学习C#中的多核编程。遇到了一个问题,我觉得这个问题应该很容易处理,而且可以利用多核来提高速度。然而在运行程序时,我发现单核实现比多核更快。该死!我错过了什么吗?
请注意我正在使用同一台电脑。
问题:给定一个数字,找出从0到该数字的所有阶乘。
我的解决方案:由于我有一个4核CPU,我将我的实现硬编码为使用4个任务。这些任务均匀地分配了数字。如果有100个数字,每个任务处理大约25个数字(任务1处理0、4、8、12...,任务2处理1、4、9、13...)。
以下是我的解决方案(似乎最大输入数字为9999):
public class Factorial { public int TotalNoOfElements { get; set; } public BigInteger GetFactorial(BigInteger number) { if (number == 0) return 0; if (number == 1) return 1; return number * GetFactorial(number - 1); } public BigInteger[] GetFactorialsUsingSingleCore(int number) { BigInteger[] factorials = new BigInteger[number + 1]; factorials[0] = 0; for (int index = 1; index <= number; index++) { Factorial f = new Factorial(); //TODO: remove. Throws stackoverflow exception @ GetFactorial() if local method is used directly. factorials[index] = f.GetFactorial(index); } return factorials; } public BigInteger[] GetFactorialsForNumbers(int[] numbers) { BigInteger[] factorials = new BigInteger[TotalNoOfElements + 1]; foreach (int item in numbers) { Factorial f = new Factorial(); //TODO: remove. Throws stackoverflow exception @ GetFactorial() if local method is used directly. factorials[item] = f.GetFactorial(item); } return factorials; } public BigInteger[] GetFactorialsUsingMultiCores(int number) { //int noOfCores = 4; StopWatchHelper stopWatchHelper = new StopWatchHelper(); stopWatchHelper.StartWatch(); BigInteger[] factorials = new BigInteger[TotalNoOfElements + 1]; ListsplitNumbersList = SplitNumbersByTasks(number); stopWatchHelper.StopWatch(); stopWatchHelper.ShowElapsedTimeInConsole("Creating list..."); Task t1 = Task.Factory.StartNew( () => GetFactorialsForNumbers(splitNumbersList[0]) ); Task t2 = Task.Factory.StartNew( () => GetFactorialsForNumbers(splitNumbersList[1]) ); Task t3 = Task.Factory.StartNew( () => GetFactorialsForNumbers(splitNumbersList[2]) ); Task t4 = Task.Factory.StartNew( () => GetFactorialsForNumbers(splitNumbersList[3]) ); Task[] tasks = { t1, t2, t3, t4 }; Task.WaitAll(tasks); stopWatchHelper.StartWatch(); //Consolidate result arrays into 1 single array foreach (Task task in tasks) { BigInteger[] result = task.Result; for (int index = 0; index < result.Length; index++) { if(result[index] != 0) factorials[index] = result[index]; } } stopWatchHelper.StartWatch(); stopWatchHelper.ShowElapsedTimeInConsole("Consolidating results..."); return factorials; } //Hardcoded for now //Returns #ofArrays = InputNumber/TotalTasks such that the work is evenly divided to the Tasks //Given an input number, we shall split it evenly across different Tasks. //With input as 25, Task 1 gets -> 0, 4, 8, 12, ... and Task 2 gets 1, 5, 9, 13, ... List SplitNumbersByTasks(int number) { int noOfCores = 4; List splitNumbersList = new List (); int reminder = number % noOfCores; for (int i = 0; i < noOfCores; i++) { if (reminder == 0) { splitNumbersList.Add(new int[(number / noOfCores)]); } else { splitNumbersList.Add(new int[(number / noOfCores) + 1]); reminder--; } } int arrayIndex = 0; for (int index = 1; index <= number; index++) { splitNumbersList[0][arrayIndex] = index++; if (index > number) break; splitNumbersList[1][arrayIndex] = index++; if (index > number) break; splitNumbersList[2][arrayIndex] = index++; if (index > number) break; splitNumbersList[3][arrayIndex] = index; ++arrayIndex; } return splitNumbersList; } } class StopWatchHelper { Stopwatch stopWatch = new Stopwatch(); public void StartWatch() { stopWatch.Reset(); stopWatch.Start(); } public void StopWatch() { stopWatch.Stop(); } public void ShowElapsedTimeInConsole(string message) { Console.WriteLine(message); Console.WriteLine(new string('-', message.Length)); Console.WriteLine("Minutes: " + stopWatch.Elapsed.Minutes); Console.WriteLine("Seconds: " + stopWatch.Elapsed.Seconds); Console.WriteLine("Milliseconds: " + stopWatch.Elapsed.Milliseconds); Console.WriteLine(); } } class Program { static void Main(string[] args) { int input; string strInput; bool isNumber; StopWatchHelper helper = new StopWatchHelper(); Factorial factorial = new Factorial(); //Get input recursively until input is not a number do { strInput = Console.ReadLine(); Console.WriteLine(); isNumber = Int32.TryParse(strInput, out input); factorial.TotalNoOfElements = input; if (isNumber) { helper.StartWatch(); BigInteger[] factorialsUsingMultiCore = factorial.GetFactorialsUsingMultiCores(input); helper.StopWatch(); helper.ShowElapsedTimeInConsole("Factorials computed using Multicore"); helper.StartWatch(); BigInteger[] factorialsUsingSingleCore = factorial.GetFactorialsUsingSingleCore(input); helper.StopWatch(); helper.ShowElapsedTimeInConsole("Factorials computed using Singlecore"); } Console.WriteLine(); } while (isNumber); } }
助手类:
class StopWatchHelper { Stopwatch stopWatch = new Stopwatch(); public void StartWatch() { stopWatch.Reset(); stopWatch.Start(); } public void StopWatch() { stopWatch.Stop(); } public void ShowElapsedTimeInConsole(string message) { Console.WriteLine(message); Console.WriteLine(new string('-', message.Length)); Console.WriteLine("Minutes: " + stopWatch.Elapsed.Minutes); Console.WriteLine("Seconds: " + stopWatch.Elapsed.Seconds); Console.WriteLine("Milliseconds: " + stopWatch.Elapsed.Milliseconds); Console.WriteLine(); } }
主类:
class Program { static void Main(string[] args) { int input; string strInput; bool isNumber; StopWatchHelper helper = new StopWatchHelper(); Factorial factorial = new Factorial(); //Get input recursively until input is not a number do { strInput = Console.ReadLine(); Console.WriteLine(); isNumber = Int32.TryParse(strInput, out input); factorial.TotalNoOfElements = input; if (isNumber) { helper.StartWatch(); BigInteger[] factorialsUsingMultiCore = factorial.GetFactorialsUsingMultiCores(input); helper.StopWatch(); helper.ShowElapsedTimeInConsole("Factorials computed using Multicore"); helper.StartWatch(); BigInteger[] factorialsUsingSingleCore = factorial.GetFactorialsUsingSingleCore(input); helper.StopWatch(); helper.ShowElapsedTimeInConsole("Factorials computed using Singlecore"); } Console.WriteLine(); } while (isNumber); } }