【问题标题】:String concatenation optimisation字符串连接优化
【发布时间】:2011-06-12 08:03:20
【问题描述】:

我们目前正在使用 LINQ 生成 SQL 查询,其中包含一些魔法来处理特定于案例的查询。

到目前为止,一切正常;非常快,几乎没有任何问题。我们最近在从数据库中查询大量数据时遇到了效率问题。

我们这样构造查询:

var someIntList = new List<int> { 1,2,3,4,5 };
var query = dtx.Query.Containers.Where(c => c.ContainerID.IsIn(someIntList));

var someStringList = new List<int> {"a", "b", "c" };
query = dtx.Query.Containers.Where(c => c.BuildingName.IsIn(someStringList));

这会产生(以及一堆与此无关的其他东西):

SELECT * FROM Container WHERE ContainerID IN (1,2,3,4,5)

SELECT * FROM Container WHERE BuildingName IN ('a','b','c')

现在在这种特殊情况下,我们需要返回 50,000 行 .. 这是通过 5 个单独的查询生成的,分担负载。 数据库返回相当快(几秒钟内),但是生成查询需要 很长 时间。

这是生成此特定查询时调用的最后一个函数:

private static string GetSafeValueForItem(object item)
{
    if (item == null)
        return "NULL";

    if (item is bool)
        return ((bool)item ? "1" : "0");
    if (item is string)
        return string.Format("'{0}'", item.ToString().Replace("'", "''"));
    if (item is IEnumerable)
        return ListToDBList((IEnumerable)item);
    if (item is DateTime)
        return string.Format("'{0}'", ((DateTime)item).ToString("yyyy-MM-dd HH:mm:ss"));

    return item.ToString();
}

private static string ListToDBList(IEnumerable list)
{
    var str = list.Cast<object>().Aggregate("(", (current, item) => current + string.Format("{0},", GetSafeValueForItem(item)));
    str = str.Trim(',');
    str += ")";
    return str;
}

在这种情况下,是否有任何明显的改进可以加快字符串连接的速度?重构代码并使用不同的实现(例如避免生成查询并直接访问数据库)不是首选,但如果它提供了很大的性能提升,那就太好了。

【问题讨论】:

  • 不知道为什么你在做 list.Cast 当一个普通的 IEnumerable 无论如何都会是对象时。

标签: c# sql mysql optimization


【解决方案1】:

不知道你为什么要做 list.Cast 当一个普通的 IEnumerable 无论如何都会是对象时。但是您的整个 ListToDBList 可以替换为

string.Format("({0})", string.Join(",",list.ToArray())); 

不确定它会快多少,但我的想法更清楚。

【讨论】:

  • 强制转换是必需的,因为 IEnumerable 没有实现 IEnumerable。 (它不能——这将是一个循环继承图。)至少对我来说,在 .NET 3.5 上,如果不先使用 Cast 将其转换为 IEnumerable,我不能将任何 Linq 扩展方法应用于 IEnumerable。
【解决方案2】:

这是一个使用 String.Join 的示例,其输出与您的 ListToDBList 相同:

String.Format("({0})", String.Join(",", list.Cast<object>().Select(item=>GetSafeValueForItem(item)).ToArray()));

请参阅此处了解为什么使用 +(这是您对 Aggregate 的调用所做的)在循环中连接速度很慢:http://www.yoda.arachsys.com/csharp/stringbuilder.html

【讨论】:

    【解决方案3】:

    我没有制作测试用例和分析你的代码,所以我不知道你能期待多少改进。

    使用 StringBuilder 代替 String.Format 和 += 运算符。已知 += 运算符很慢。我怀疑 String.Format 也会有点慢。

    您也可以尝试使用 string.Join 而不是手动加入数组。它适用于较新版本的 .NET 框架(4.0?)中的 IEnumerable。

    【讨论】:

    • String.Format 在内部使用 StringBuilder (stackoverflow.com/questions/6785/…),但由于它被多次使用,我同意你的观点。
    • Multiple string.format 将与 += 一样慢,因为您重复分配新内存并复制数据。字符串生成器仅在您执行 ToString 调用时最后分配内存和副本。
    【解决方案4】:

    您的聚合代码基本上是循环中的字符串连接。不要那样做。

    选项:

    1. 使用StringBuilder
    2. 使用string.Join

    【讨论】:

    • 解释这背后的原因:字符串连接为每个连接分配一个新的内存缓冲区,将两个字符串复制到该缓冲区并将新缓冲区分配给变量。循环长列表会导致大量内存分配和复制,这是非常昂贵的性能。 StringBuilder 和 string.join 预先计算所有元素所需的总空间并只复制一次。
    猜你喜欢
    • 2017-06-10
    • 1970-01-01
    • 2010-09-29
    • 2020-02-29
    • 1970-01-01
    • 2017-03-09
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多