网络应用编程 实验3 矩阵并行计算练习实验

一、实验要求

编写一个WPF应用程序,利用数据并行计算两个矩阵(M×N和N×P)的乘积,得到一个M×P的矩阵。

 

具体要求

(1)在代码中用多任务通过调用某方法实现矩阵并行运算,在调用的参数中分别传递M、N、P的大小。

(2)程序中至少要测试3次有代表性的不同大小的矩阵运算,并显示其并行运行用时

二、实验结果展示

 

 

三、实验代码展示

1.xml部分

<Window x:Class="实验报告3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:实验报告3"
        mc:Ignorable="d"
        Title="利用Parallel.For实现矩阵相乘" Height="450" Width="894">
    <Grid Background="AliceBlue">
        <DockPanel>
            <StackPanel DockPanel.Dock="Top">
                <Border BorderThickness="5" Margin="2" BorderBrush="Red" VerticalAlignment="Center">
                    <TextBlock Text="利用数据并行计算两个矩阵(M*N和N*P)的乘积" FontSize="25" FontFamily="微软雅黑" HorizontalAlignment="Center"/>
                </Border>
            </StackPanel>
            <ScrollViewer DockPanel.Dock="Top" Height="335">
                <DockPanel>
                    <StackPanel DockPanel.Dock="Left" Width="105" VerticalAlignment="Center">
                        <Label Content="M值:" Width="60" FontSize="18" Background="AliceBlue" Margin="10"/>
                        <Label Content="N值:" Width="60" FontSize="18" Background="AliceBlue" Margin="10"/>
                        <Label Content="P值:" Width="60" FontSize="18" Background="AliceBlue" Margin="10"/>
                    </StackPanel>

                    <StackPanel DockPanel.Dock="Left" Width="200" VerticalAlignment="Center">
                        <Border BorderThickness="5" Margin="2" BorderBrush="Orange">
                            <TextBox x:Name="t1" Text=""  FontSize="18" Margin="1"/>
                        </Border>

                        <Border BorderThickness="5" Margin="2" BorderBrush="Orange">
                            <TextBox x:Name="t2" Text=""  FontSize="18" Margin="1"/>
                        </Border>

                        <Border BorderThickness="5" Margin="2" BorderBrush="Orange">
                            <TextBox x:Name="t3" Text=""  FontSize="18" Margin="1"/>
                        </Border>
                    </StackPanel>

                    <StackPanel DockPanel.Dock="Right">
                        <TextBlock x:Name="t4" Text="结果显示:" FontSize="18" FontFamily="楷体" Background="Black" Foreground="AliceBlue" Height="331" Width="534"/>
                    </StackPanel>
                </DockPanel>
            </ScrollViewer>
            <Border DockPanel.Dock="Bottom" HorizontalAlignment="Center">
                <Button Content="运行" Width="70" Height="30" Background="BlueViolet" Click="Button_Click"/>
            </Border>
        </DockPanel>
    </Grid>
</Window>

2.cs部分

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace 实验报告3
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        //实验次数
        int x = 0;
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            //接收M,N,P
            int aRow, aCol, bCol;

            int.TryParse(t1.Text, out aRow);
            int.TryParse(t2.Text, out aCol);
            int.TryParse(t3.Text, out bCol);
            //形成要用的两个矩阵ab ,里面存储的数是double类型的,当然在额外创建一个矩阵c接收结果
            double[,] a, b, c;

            a = initMatrix(aRow, aCol);
            b = initMatrix(aCol, bCol);
            c = new double[aRow, bCol];
            //进行矩阵并行运算,并计算时间
            long time1 = await Task.Run(() => timeSpan(a, b, c));
            //将实验次数++
            x++;
            //将计算的值在右侧黑色文本框中显示出来
            t4.Text += string.Format("\n测试{0}(矩阵1:{1}*{2},矩阵2:{3}*{4}),用时:{5}毫秒", x, aRow, aCol, aCol, bCol, time1);
        }

        //初始化矩阵
        public double[,] initMatrix(int row, int column)
        {
            Random random= new Random();

            double[,] matrix = new double[row,column];
            for (int i = 0; i < row; i++) 
            {
                for (int j = 0; j < column; j++) 
                {
                    matrix[i,j] = random.Next(100);
                }
            }
            return matrix;
        } 
        //并行矩阵乘法
        public void ParallelMatrixMultiplication(double[,] a, double[,] b, double[,] c) 
        {
            //获取两个矩阵的行列数,因为TryParse好像用不到全局变量里面,所以只能写的这么复杂了
            int aRows = a.GetLength(0);
            int aCols = a.GetLength(1);
            int bCols = b.GetLength(1);
            //计算并输出,但是 数太大 会产生 数组越界异常

            // i是a行,j是b列,k是a列b行
            Action<int> action = i =>
            {
                for (int j = 0; j < bCols; j++) 
                {
                    double temp = 0;//用一个临时变量可提高并行效率
                    for (int k = 0; k < aCols; k++) 
                    {
                        temp += a[i, k] * b[k, j];
                    }
                    c[i, j] = temp;
                }
            };
            //action只是定义了一个方法,所以是不是自动运行的,真正运行的代码是下面这行 并行运行代码,这也解释了i的取值是多少的问题
            Parallel.For(0, aRows , action);
        }
        //计算运算时间
        public long timeSpan(double[,] a, double[,] b, double[,]c)
        {
            Stopwatch sw = Stopwatch.StartNew();
            ParallelMatrixMultiplication(a, b, c);
            sw.Stop();
            return sw.ElapsedMilliseconds;
        }
    }
}

四、实验总结

1. 为什么要用panel控件(各种panel控件),明明用了跟没用一样。
   
    答: 简单的说,panel控件是一个容器控件,你可以在上面放置别的控件,当做一个Form用。

            应用程序会尽可能将一个面板中的所有控件分页到同一屏幕上。通过将控件分组到 Panel 控件中,      应用程序还可使用单个命令隐藏或显示一组控件。

            当移动 Panel 控件时,它包含的所有控件也将移动

2. 所以Form是什么意思?
    简单来讲就是Windows窗体应用,创建wpf自动生成的xml界面,也可以独立添加新的。

    官方一点来说就是:
           1.窗体也是对象,窗体类定义了生成窗体的模板,每当实例化一个窗体类,就产生一个窗体

           2.Form类是所有窗体类的基类。

           3.在一个项目中,每个窗体都有自己的Form.cs代码,但所有窗体只有一个启动窗体,核心便是    Program.cs文件里的Main()函数作为程序的主入口点。

3. textbox和textblock的区别
 前者可以编辑,后者不可以。

4. task.Run 其实在读程序时,可以直接读成“运行.....”

5.与界面交互,例如:将结果在界面中显示出来

6. cts = new System.Threading.CancellationTokenSource();
    放在task(任务,cts.token) 用于侦听取消通知

7. 可以直接用int.TryParse(a,out b); 接收文本框中的值

8. Action本身是一种方法,后面必须加分号.
    Action<>是没有返回值,而有没有参数取决于<>有没有值类型,如有int,就是含有一个int型的参数,这时候就可以像for循环里面 int i=0; 随便使用 i 这个参数了。

9.本次实验有个小问题,就是结果数组写错了,写成了a列b列了,一直导致数组越界异常,耽误了两个多小时。。。。。

五、书上原码展示

1.xml部分

<Window x:Class="Experiment3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="利用Parallel.For实现矩阵相乘" Height="350" Width="525">
    <DockPanel>
        <Border DockPanel.Dock="Bottom" Background="Beige" Padding="0 5 0 5">
            <Button Name="btnStart" Padding="20 0 20 0" HorizontalAlignment="Center" Content="开始" Click="btnStart_Click"/>
        </Border>
        <ScrollViewer>
            <StackPanel Background="White" TextBlock.LineHeight="30">
                <TextBlock Name="textBlock1" Margin="0 10 0 0" TextWrapping="Wrap"/>
            </StackPanel>
        </ScrollViewer>
    </DockPanel>
</Window>

2.cs部分

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Experiment3
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {

        Stopwatch stopwatch = new Stopwatch();

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnStart_Click(object sender, RoutedEventArgs e)
        {
            long[] t1 = await Task.Run(() => Multiply(200, 18, 27));
            textBlock1.Text = string.Format("测试1(矩阵1:200×18,矩阵2:18×27),用时:{1}毫秒", t1[0], t1[1]);

            long[] t2 = await Task.Run(() => Multiply(2000, 180, 270));
            textBlock1.Text += string.Format("\n测试2(矩阵1:2000×180,矩阵2:180×270),用时:{1}毫秒", t2[0], t2[1]);

            long[] t3 = await Task.Run(() => Multiply(3000, 200, 300));
            textBlock1.Text += string.Format("\n测试3(矩阵1:2000×200,矩阵2:200×300),用时:{1}毫秒", t3[0], t3[1]);
        }

        private long[] Multiply(int rowCount, int colCount, int colCount2)//参数分别是M,N,P
        {
            long[] timeElapsed = new long[2];
            //初始化两个矩阵
            double[,] m1 = InitializeMatrix(rowCount, colCount);
            double[,] m2 = InitializeMatrix(colCount, colCount2);
            //乘之后得到的矩阵,这里相当于手动取得
            double[,] result = new double[rowCount, colCount2];

            // 并行,
            //计时开始
            stopwatch.Restart();
            result = new double[rowCount, colCount2];//M*P

            MultiplyMatricesParallel(m1, m2, result);
            //计时结束
            stopwatch.Stop();
            //返回时间
            timeElapsed[1] = stopwatch.ElapsedMilliseconds;
            return timeElapsed;
        }

        #region 并行
        /// <summary>
        /// 计算两个矩阵的乘积
        /// </summary>
        /// <param name="a">矩阵a</param>
        /// <param name="b">矩阵b</param>
        /// <param name="result">相乘的结果</param>
        public static void MultiplyMatricesParallel(double[,] a, double[,] b, double[,] result)
        {
            //如果是64位机,将int改为Int64可提高性能,但修改后将无法在32位机器上运行
            int aRows = a.GetLength(0);//a的行
            int aCols = a.GetLength(1);//a的列,b的行
            int bCols = b.GetLength(1);//b的列

            // 内循环不需要并行
            Action<int> action = i =>
            {
                for (int j = 0; j < bCols; j++)
                {
                    double temp = 0; //用一个临时变量可提高并行效率
                    for (int k = 0; k < aCols; k++)
                    {
                        temp += a[i, k] * b[k, j];
                    }
                    result[i, j] = temp;
                }
            };
            // 外循环并行执行
            Parallel.For(0, aRows, action);//实际上就是矩阵a每行都单独去运算,最后结果汇总,上面action中的i就是a的行arows,比如第一行去跟b的j列运算,同时第二(i)行野区和b的j列运算
        }
        #endregion

        public static double[,] InitializeMatrix(int rows, int cols)//初始化矩阵
        {
            double[,] matrix = new double[rows, cols];

            Random r = new Random();
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    matrix[i, j] = r.Next(100); //行列对应的数随机设置
                }
            }
            return matrix;
        }
    }
}

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值