C# - 提高搜索时的性能
Posted
技术标签:
【中文标题】C# - 提高搜索时的性能【英文标题】:C# - Improve performace when searching 【发布时间】:2018-12-10 20:30:59 【问题描述】:我在 txt 文件中有一个包含 15000000 个用户名的列表,我编写了一个方法来创建大脑钱包,检查是否有任何地址包含 600 个地址的列表。差不多就是这样
private static List<string> userList = new List<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoUser-workspace-db.txt"));
private static List<string> enterpriseUserList = new List<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt"));
foreach (var i in userList)
userid = ToAddress(i);
if (enterpriseUserList.Contains(userid))
Console.WriteLine(i,userid);
private string ToAddress(string username)
string bitcoinAddress = BitcoinAddress.GetBitcoinAdressEncodedStringFromPublicKey(new PrivateKey(Globals.ProdDumpKeyVersion, new SHA256Managed().ComputeHash(UTF8Encoding.UTF8.GetBytes(username), 0, UTF8Encoding.UTF8.GetBytes(username).Length), false).PublicKey);
ToAddrsess 方法将用户名哈希为 SHA256 字符串,获取其公钥并将其转换为如下地址:
15hDBtLpQfcbrrAFupWjgN5ieHeEBd8mbu
这段代码很糟糕,运行速度很慢,每秒处理大约 200 行数据。所以我尝试使用多线程来改进它
private static void CheckAddress(string username)
var userid = ToAddress(username);
if (enterpriseUserList.Contains(userid))
Console.WriteLine(i,userid);
private static void Parallel()
List<string> items = new List<string>(File.ReadLines(@"C:\Users\Erik\Desktop\InfernoUser-workspace-db.txt"));
ParallelOptions check = new ParallelOptions() MaxDegreeOfParallelism = 100 ;
Parallel.ForEach<string>(items, check, line =>
CheckAddress(line);
);
这并没有太大帮助。有人可以建议如何即兴创作吗?与在 CPU 上运行的 vanitygen 相比,它每秒可以处理 4-500k 个地址。怎么会有这么大的区别?
【问题讨论】:
Contains
进行线性搜索,您的算法基本上运行在 O(N×M) 中,如果它可以使用某种索引会快很多。
@Bart Friederichs 你能说得更具体点吗?
您可以解码企业列表中的 600 并进行比较,而不是对用户列表中的 1500 万进行编码吗?
@Jimmy 这不是 SHA256 的工作方式,您不能保留它。我从userList中随机选择了600个用户名,转换成地址做企业列表
好的,错过了 sha256 部分
【参考方案1】:
您可以尝试使用带有 key=userid 的 Dictionary,以防止每次迭代都按列表搜索
var dict = new ConcurrentDictionary<string, string>(100, userList.Count);
userList.AsParallel().ForAll(item =>
dict.AddOrUpdate(ToAddress(item), item, (key,value)=>return value;);
);
enterpriseUserList.AsParallel().ForAll(x =>
if (dict.ContainsKey(x))
Console.WriteLine(dict[x]);
);
【讨论】:
谢谢,如果你对加密货币有任何了解,你能知道有什么方法可以更快地创建大脑钱包。我的代码大约需要 200 毫秒来创建一个 AddOrUpdate 有错误,它说我不能接受 2 个参数。我该如何解决这个问题? 第二个.AsParallel()
可能浪费多于帮助。
@RandRandom 你能推荐其他解决方案吗?我只是切换到 MikkaRin 方式,它使我的代码每秒运行更多大约 260 行用户名。我猜是什么
@HuangLee - 不争论第一个 .AsParallel()
代码块女巫会带来一些好处,但第二个不会表现良好 - Console.WriteLine
部分将同步并阻止并行执行 - 另外没有任何反应那里需要并行执行。一个普通的foreach
会表现得更好。 - 根据对Console.WriteLine
的调用次数,您应该考虑切换到字符串生成器并只调用一次Console.WriteLine(stringBuilder.ToString());
。【参考方案2】:
在寻找效率低下时,主要的危险信号之一是重复的函数调用。您拨打GetBytes
两次。将它放入一个单独的变量中并调用一次应该会有所帮助。
private string ToAddress(string username)
var userNameAsBytes = UTF8Encoding.UTF8.GetBytes(username);
string bitcoinAddress = BitcoinAddress.GetBitcoinAdressEncodedStringFromPublicKey(new PrivateKey(Globals.ProdDumpKeyVersion, new SHA256Managed().ComputeHash(userNameAsBytes, 0, userNameAsBytes.Length), false).PublicKey);
【讨论】:
我只是用秒表测试它,它几乎是一样的:|【参考方案3】:你可以在这里进行一些操作
-
将
List
更新为HashSet
。它将显着执行Contains
操作。我确信这是这个代码库中最慢的情况。
private static List<string> enterpriseUserList = new List<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt"));
改成
private static HashSet<string> enterpriseUserList = new HashSet<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt"));
不要使用ParallelOptions check = new ParallelOptions() MaxDegreeOfParallelism = 100 ;
这种优化会增加上下文切换并降低性能。
使用Partitioner.Create
优化Parallel.ForEach
也许这就是我能给你的建议。
private static List<string> userList = new List<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoUser-workspace-db.txt"));
private static HashSet<string> enterpriseUserList = new HashSet<string>(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt"));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CheckAddress(int id,string username)
var userid = ToAddress(username);
if (enterpriseUserList.Contains(userid))
// todo
private static void Parallel()
var ranges = Partitioner.Create(0,userList.Count);
Parallel.ForEach(ranges ,(range)=>
for(int i=range.Item1;i<range.Item2;i++)
CheckAddress(i,userList[i])
【讨论】:
以上是关于C# - 提高搜索时的性能的主要内容,如果未能解决你的问题,请参考以下文章
提高使用 CreateDocumentQuery 和 ExecuteNextAsync 时的性能