Ric*_*ell 5 .net c# dynamics-crm
我有一个已反序列化并粘贴到HashSet中的Dynamics CRM 2013审核数据列表,定义为:
private class AuditCache
{
public Guid ObjectId;
public int HistoryId;
public DateTime? DateFrom;
public DateTime? DateTo;
public string Value;
};
private HashSet<AuditCache> _ac = new HashSet<AuditCache>();
Run Code Online (Sandbox Code Playgroud)
我添加这样的数据(来自SQL Server记录集):
_ac.Add(new AuditCache{
ObjectId = currentObjectId,
HistoryId = Convert.ToInt32(dr["HistoryId"]),
DateTo = Convert.ToDateTime(dr["CreatedOn"]),
Value = value});
Run Code Online (Sandbox Code Playgroud)
我最终得到了大约一百万条记录。
接下来,我需要遍历每个Guid,并从匹配的审计数据中提取数据的子集。我有一份我在其他地方生成的Guid列表,大约有30万个要处理。我将它们存储在:
var workList = new Dictionary<Guid, DateTime>();
Run Code Online (Sandbox Code Playgroud)
...并像这样遍历它们:
foreach (var g in workList)
Run Code Online (Sandbox Code Playgroud)
然后,我需要执行此操作以提取每个Guid的子集:
List<AuditCache> currentSet = _ac.Where(v => v.ObjectId == g.Key).ToList();
Run Code Online (Sandbox Code Playgroud)
但这很慢。
填充我的初始审核数据列表大约需要1分钟,但要花费几个小时(我从未运行过它才能完成,所以这是基于处理1%数据的时间)来取出每个数据集,对其进行处理并将其喷回进入数据库表。
单步执行代码,我可以看到瓶颈似乎正在从每个Guid的列表中拉出子集。所以我的问题是,是否有更好/更有效的方法(架构?)来存储/检索我的数据集?
需要注意的一件事是,我知道Guid本质上对索引/搜索的速度很慢,但是由于Dynamics CRM的工作方式,我在使用它们方面受到了很大的限制。我想我可以创建一个词典来查找Guid并将其“转换”为整数值或类似的东西,但是我不相信这会有所帮助吗?
编辑
好的,我使用实时数据(371,901吉德)测试了三种解决方案,这些结果是每1,000吉德的平均时间。请注意,这包括对SQL Server的处理/ INSERT,因此它不是适当的基准。
Method #0 - List with Lambda ~30.00s per 1,000 rows (I never benchmarked this precisely)
Method #1 - IntersectWith 40.24s per 1,000 rows (cloning my Hashset spoilt this)
Method #2 - BinarySearch 3.20s per 1,000 rows
Method #3 - Generic Dictionary 2.19s per 1,000 rows
Run Code Online (Sandbox Code Playgroud)
在此基础上,我可能会从头开始重写代码,因为我认为我采用的整个方法都不正确。
但是,这是一个非常有用的学习活动,非常感谢所有贡献者。我将接受BinarySearch作为正确的答案,因为它可以满足我的要求,并且比原始代码要快得多。
只需在此处明确说明,IntersectWith确实“抽烟”得很快,但是它对我的特定问题不起作用,因为我需要不断返回到原始哈希集。
P4在1.2秒内达到100万
HashSet具有IntersectWith
吸烟速度很快
如果由另一个参数表示的集合是具有与当前HashSet对象相同的相等比较器的HashSet集合,则此方法是O(n)操作。否则,此方法是O(n + m)运算,其中n是Count,m是其他元素的数量。
但是要使其正常工作,您需要AuditCache来实现Object并重写GetHashCode和Equals
Object.GetHashCode方法
GUID很好地进行了哈希处理(最小冲突),因此这将非常快。
WorkList也将需要是AuditCache(即使它实际上不是AuditCache),
也可以让它们都实现一个使用Guid ObjectId作为键的类(Equal和GetHashCode)。
如果将GUID用作哈希键(字典和哈希集),则其索引编制不会天生就很慢-它是一个很好的键,因为它几乎没有(或没有)冲突。即使您同时将这两个字典作为具有Guid键的字典,也会更快。但是Dictionary没有IntersectWith。
在P4上的1.2秒内达到100万个,克隆0.5秒,相交0.7秒
using System.Diagnostics;
namespace HashSetIntersect
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Stopwatch sw = new Stopwatch();
sw.Start();
HashSet<AuditCache> TestHashKeys1 = new HashSet<AuditCache>();
HashSet<AuditCache> TestHashKeys2 = new HashSet<AuditCache>();
for (UInt32 i = 0; i < 1000000; i++)
{
Guid g = Guid.NewGuid();
TestHashKeys1.Add(new AuditCache(g, 1, (DateTime?)null, (DateTime?)null, "value1"));
if (i % 2 == 0) TestHashKeys2.Add(new AuditCache(g, 0, (DateTime?)null, (DateTime?)null, "value2"));
}
Debug.WriteLine(TestHashKeys1.Count.ToString() + " " + TestHashKeys2.Count.ToString());
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds.ToString());
sw.Restart();
HashSet<AuditCache> TestHashKeys3 = new HashSet<AuditCache>(TestHashKeys1);
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds.ToString());
sw.Restart();
TestHashKeys3.IntersectWith(TestHashKeys2);
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds.ToString());
foreach (AuditCache ac in TestHashKeys3)
{
Debug.WriteLine(ac.Value);
}
}
}
public abstract class HashKey : Object
{
public Guid ObjectId { get; private set; }
public override bool Equals(object obj)
{
if (!(obj is HashKey)) return false;
HashKey comp = (HashKey)obj;
return this.ObjectId == comp.ObjectId;
}
public override int GetHashCode()
{
return ObjectId.GetHashCode();
}
public HashKey(Guid objectId)
{
ObjectId = objectId;
}
}
public class TestHashKey : HashKey
{
public TestHashKey(Guid ObjectId)
: base(ObjectId)
{ }
}
public class AuditCache : HashKey
{
public int HistoryId { get; private set; }
public DateTime? DateFrom { get; private set; }
public DateTime? DateTo { get; private set; }
public string Value { get; private set; }
public AuditCache(Guid objectId, int historyId, DateTime? dateFrom, DateTime? dateTo, string value)
: base(objectId)
{
HistoryId = historyId;
DateFrom = dateFrom;
DateTo = dateTo;
Value = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您按 GUID(毕竟是一个大整数)对 AuditCache 列表进行排序,然后使用List<T>.BinarySearch它,怎么样?
我得到了相当好的结果(在 i3-3110M @2.4Ghz 上不到 15 秒)。累计次数如下:
下面我使用BigIntegerfromSystem.Numerics将 Guid 解释为 128 位整数。
如果我没有遗漏什么,那么这应该可行。请注意,查找 for 循环实际上是最坏的情况,因为不太可能发生冲突(因此索引始终为 -1)。在你的情况下,它可能会更快:
class AuditCache
{
public Guid ObjectId;
public int HistoryId;
public DateTime? DateFrom;
public DateTime? DateTo;
public string Value;
};
class AuditCacheComparer : IComparer<AuditCache>
{
public int Compare(AuditCache x, AuditCache y)
{
BigInteger intx = new BigInteger(x.ObjectId.ToByteArray());
BigInteger inty = new BigInteger(y.ObjectId.ToByteArray());
if (intx < inty)
{
return -1;
}
else if (intx > inty)
{
return 1;
}
return 0;
}
}
class Program
{
static void Main(string[] args)
{
List<AuditCache> testCollection = new List<AuditCache>();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i != 1000000; ++i)
{
testCollection.Add(new AuditCache() { ObjectId = Guid.NewGuid(), HistoryId = i });
}
Console.WriteLine("Collection created: {0} ms", sw.ElapsedMilliseconds);
AuditCacheComparer comparer = new AuditCacheComparer();
testCollection.Sort(comparer);
Console.WriteLine("Collection sorted: {0} ms", sw.ElapsedMilliseconds);
for(int i = 0; i != 300000; ++ i)
{
var index = testCollection.BinarySearch(new AuditCache() {ObjectId = Guid.NewGuid()}, comparer);
if (index > 0)
{
Console.WriteLine("Found: {0} ms", sw.ElapsedMilliseconds);
}
}
Console.WriteLine("Lookup: {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)