2012年11月20日火曜日

CSVのパース速度を比較する


正規表現を使った場合と使わない場合の処理速度を比較します。
正規表現を使う場合のアルゴリズムは「CSVファイルを読み込んでDataGridに表示」の DataTable GetCsvDataTable(string content) メソッドを、
使わない場合のアルゴリズムは「正規表現を使わないでCSVをパース」の DataTable GetCsvDataTable(string content) メソッドを使ってます。
下のソースでは、それぞれのメソッド名を、GetCsvDataTableRegex、GetCsvDataTableNoRegex に変更しています。
Task.Factory.StartNew で 非同期処理」の方法で、2つのCSVのパースを並列処理し、処理時間をテキストボックスに表示します。
左が正規表現を使った場合、右が使わない場合です。
正規表現を使わないアルゴリズムの方が5倍ほど速いようです。
テストデータは、市町村コードをコピーして10万行くらいに増やしています。




MainWindow.xaml

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="350" Width="625">
    <Window.CommandBindings>
        <CommandBinding Command="Open" Executed="OpenCommandHandler"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Menu Grid.Row="0">
            <MenuItem Command="Open" />
        </Menu>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid Grid.Column="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="50" />
                </Grid.RowDefinitions>
                <DataGrid Grid.Row="0" Name="dataGrid1" />
                <TextBox Grid.Row="1" Name="textBox1"
                         Text="{Binding Path=StatusAndElapsedTime,Mode=OneWay}"
                         TextChanged="textBox1_TextChanged" />
            </Grid>
            <Grid Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="50" />
                </Grid.RowDefinitions>
                <DataGrid Grid.Row="0" Name="dataGrid2" />
                <TextBox Grid.Row="1" Name="textBox2"
                         Text="{Binding Path=StatusAndElapsedTime,Mode=OneWay}"
                         TextChanged="textBox2_TextChanged" />
            </Grid>
        </Grid>
    </Grid>
</Window>


MainWindow.xaml.cs


using Microsoft.Win32;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;

namespace WpfApplication3
{
    public partial class MainWindow : Window
    {
        private Message message1 = new Message();
        private Message message2 = new Message();
        private Task<DataTable> task1;
        private Task<DataTable> task2;

        public MainWindow()
        {
            InitializeComponent();

            this.textBox1.DataContext = this.message1;
            this.textBox2.DataContext = this.message2;
        }

        private void OpenCommandHandler(object sender, ExecutedRoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                string content = File.ReadAllText(ofd.FileName, Encoding.GetEncoding("Shift_JIS"));

                this.dataGrid1.ItemsSource = null;
                this.dataGrid2.ItemsSource = null;
                this.message1.Status = TaskStatus.Created;
                this.message2.Status = TaskStatus.Created;

                task1 = Task.Factory.StartNew(() => GetCsvDataTableRegex(content, this.message1));
                task2 = Task.Factory.StartNew(() => GetCsvDataTableNoRegex(content, this.message2));
            }
        }

        private static DataTable GetCsvDataTableNoRegex(string content,Message message)
        {
            message.Status = TaskStatus.Running;


            // ・・・・・・・・・・・
            // ・・・・・・・・・・・
            // ・・・・・・・・・・・


            message.Status = TaskStatus.RanToCompletion;
            return dt;
        }

        private static DataTable GetCsvDataTableRegex(string content,Message message)
        {
            message.Status = TaskStatus.Running;


            // ・・・・・・・・・・・
            // ・・・・・・・・・・・
            // ・・・・・・・・・・・


            message.Status = TaskStatus.RanToCompletion;
            return dt;
        }

        private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.message1.Status == TaskStatus.RanToCompletion)
            {
                dataGrid1.ItemsSource = task1.Result.DefaultView;
            }
        }

        private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.message2.Status == TaskStatus.RanToCompletion)
            {
                dataGrid2.ItemsSource = task2.Result.DefaultView;
            }
        }
    }
}

Message.cs


using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;

namespace WpfApplication3
{
    public class Message : INotifyPropertyChanged
    {
        public Stopwatch Stopwatch { get; set; }

        private TaskStatus status = TaskStatus.Created;
        public TaskStatus Status
        {
            get { return status; }
            set
            {
                if (value != this.status)
                {
                    if (value == TaskStatus.RanToCompletion)
                    {
                        this.Stopwatch.Stop();
                    }
                    else if (value == TaskStatus.Running)
                    {
                        this.Stopwatch.Restart();
                    }
                    this.status = value;
                    NotifyPropertyChanged("Status");
                    NotifyPropertyChanged("StatusAndElapsedTime");
                }
            }
        }

        public string StatusAndElapsedTime
        {
            get
            {
                return this.status + "\n" +
                    ((this.status == TaskStatus.RanToCompletion) ? this.Stopwatch.Elapsed.ToString() : "");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        public Message()
        {
            this.Stopwatch = new Stopwatch();
        }
    }
}



0 件のコメント:

コメントを投稿