如何改善以下代码的性能?

时间:2020-07-25 09:21:49

标签: c# algorithm performance

此代码有效,但是花费太多时间。每个数据表都包含1000行,每次我需要针对某个列从另一个数据表中过滤数据。

for (int i = 0; i < dsResult.Tables[0].Rows.Count; i++)
{
    DataTable dtFiltered = dtWorkExp.Clone();

    foreach (DataRow drr in dtWorkExp.Rows)
    {
        if (drr["UserId"].ToString() == dsResult.Tables[0].Rows[i]["Registration NO."].ToString())
        {
            dtFiltered.ImportRow(drr);
        }
    }

    DataTable dtFilteredAward= dtAwards.Clone();

    foreach (DataRow drr in dtAwards.Rows)
    {
        if (drr["UserId"].ToString() == dsResult.Tables[0].Rows[i]["Registration NO."].ToString())
        {
            dtFilteredAward.ImportRow(drr);
        }
    }

    DataTable dtFilteredOtherQual = dtOtherQual.Clone();

    foreach (DataRow drr in dtOtherQual.Rows)
    {
        if (drr["UserId"].ToString() == dsResult.Tables[0].Rows[i]["Registration NO."].ToString())
        {
            dtFilteredOtherQual.ImportRow(drr);
        }
    }

    //Do some operation with filtered Data Tables
}

3 个答案:

答案 0 :(得分:1)

您可以在for循环之外声明这些行。

  DataTable dtFiltered = dtWorkExp.Clone();

您可以将其分配给一个变量并使用它,而不必每次都访问dsResult.Table [0]。

您也可以用LINQ替换foreach循环。

答案 1 :(得分:1)

将表达式的值放在变量中。

var regNo = dsResult.Tables[0].Rows[i]["Registration NO."].ToString();

将列的索引放入变量。通过索引访问要比通过列名访问更快。

int index = dtWorkExp.Columns["UserId"].Ordinal;

结果代码:

int dtWorkIndex = dtWorkExp.Columns["UserId"].Ordinal;
int dtAwardsIndex = dtAwards.Columns["UserId"].Ordinal;
int dtOtherQualIdex = dtOtherQual.Columns["UserId"].Ordinal;

for (int i = 0; i < dsResult.Tables[0].Rows.Count; i++)
{
    var regNo = dsResult.Tables[0].Rows[i]["Registration NO."].ToString();

    DataTable dtFiltered = dtWorkExp.Clone();

    foreach (DataRow drr in dtWorkExp.Rows)
    {
        if (drr[dtWorkIndex].ToString() == regNo)
        {
            dtFiltered.ImportRow(drr);
        }
    }
    ...

当然,如果您确切地知道列索引,则可以将其设置为常量。另外,如果UserId索引在所有表中都匹配,则单个变量就足够了。


您也可以尝试使用BeginLoadDataEndLoadData方法。

DataTable dtFiltered = dtWorkExp.Clone();
dtFiltered.BeginLoadData();

foreach (DataRow drr in dtWorkExp.Rows)
{
    if (drr[dtWorkIndex].ToString() == regNo)
    {
        dtFiltered.ImportRow(drr);
    }
}
dtFiltered.EndLoadData();

但是我不确定它们是否与ImportRow一起使用。


最后,并行化会有所帮助。

for (int i = 0; i < dsResult.Tables[0].Rows.Count; i++)
{
    var regNo = ...;

    var workTask = Task.Run(() =>
    {
        DataTable dtFiltered = dtWorkExp.Clone();
    
        foreach (DataRow drr in dtWorkExp.Rows)
        {
            if (drr[dtWorkIndex].ToString() == regNo)
            {
                dtFiltered.ImportRow(drr);
            }
        }
        return dtFiltered;
    });
    
    var awardTask = Task.Run(() =>
        ...
    
    var otherQualTask = Task.Run(() =>
        ...
    
    //Task.WaitAll(workTask, awardTask, otherQualTask);
    await Task.WhenAll(workTask, awardTask, otherQualTask);
    
    //Do some operation with filtered Data Tables
}

答案 2 :(得分:1)

我会做什么:

可枚举的主数据表的所有行:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bgMain">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/RecordToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:titleTextAppearance="@style/toolbarTextSize"
        app:title="Record"
        android:theme="@style/RecordActionBar"
        android:background="@color/toolbar"
        app:titleMarginStart="20dp"
        app:titleTextColor="@color/white"
   />

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/recyclerViewRecord"
        android:layout_below="@+id/RecordToolbar"/>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/navigationBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_margin="0dp"
        android:padding="0dp"
        app:itemBackground="@color/toolbar"
        app:itemIconTint="@drawable/selector"
        app:itemTextColor="@drawable/selector"
        app:menu="@menu/bottom_nav" />
</RelativeLayout>

获取您要用于过滤的列:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bgMain"
   >

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/RecordToolbar"
        android:elevation="10dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorPrimaryDark"
            android:padding="10dp"
            >


            <TextView
                android:id="@+id/recordTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Title"
                android:layout_marginBottom="5dp"
                android:textColor="@color/white"
                android:textSize="25sp" />

            <ImageButton
                android:id="@+id/recordDelete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:background="@color/colorPrimaryDark"
                android:padding="0dp"
                android:src="@drawable/ic_delete"

                />

            <TextView
                android:id="@+id/recordCount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/recordTitle"
                android:text="Count: 44"
                android:textColor="@color/white" />

            <TextView
                android:id="@+id/recordLap"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/recordTitle"
                android:layout_marginStart="10dp"
                android:layout_toRightOf="@+id/recordCount"
                android:text="Lap: 8"
                android:textColor="@color/white" />

            <TextView
                android:id="@+id/recordLimit"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/recordTitle"
                android:layout_marginStart="10dp"
                android:layout_toRightOf="@+id/recordLap"
                android:text="Limit: 66"
                android:textColor="@color/white" />

            <TextView
                android:id="@+id/recordTotalCount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/recordTitle"
                android:layout_marginStart="10dp"
                android:layout_toRightOf="@+id/recordLimit"
                android:text="Total Count: 572"
                android:textColor="@color/white" />


        </RelativeLayout>

    </androidx.cardview.widget.CardView>

</RelativeLayout>

创建一个接受该过滤器的方法,一个要过滤的表和一个要比较的字段。

var rows = dsResult.Tables[0].AsEnumerable();

最后使用该方法过滤所有表:

var filter = rows.Select(r => r.Field<string>("Registration NO."));

总会是这样

public static DataTable Filter<T>(EnumerableRowCollection<T> filter, DataTable table, string fieldName)
{
    return table.AsEnumerable().Where(r => filter.Contains(r.Field<T>(fieldName))).CopyToDataTable();
}