【问题标题】:C# - Improve performace when searchingC# - 提高搜索时的性能
【发布时间】: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 部分

标签: c# multithreading performance cryptography text-files


【解决方案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】:

你可以在这里进行一些操作

  1. 将List 更新为HashSet。它将显着执行Contains 操作。我确信这是这个代码库中最慢的情况。 private static List&lt;string&gt; enterpriseUserList = new List&lt;string&gt;(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt")); 改成 private static HashSet&lt;string&gt; enterpriseUserList = new HashSet&lt;string&gt;(File.ReadAllLines(@"C:\Users\Erik\Desktop\InfernoEnterpriseUser-local-db.txt"));
  2. 不要使用ParallelOptions check = new ParallelOptions() { MaxDegreeOfParallelism = 100 }; 这种优化会增加上下文切换并降低性能。
  3. 使用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])               
     }}

}

【讨论】:

    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多