【问题标题】:How can I best "parallelise" a set of four nested for()-loops in a Brute-Force attack?如何在蛮力攻击中最好地“并行化”一组四个嵌套的 for() 循环?
【发布时间】:2018-06-10 18:04:58
【问题描述】:

我有以下家庭作业:
我需要使用以下掩码暴力破解 4 字符密码

%%@@

(其中 @ - 是数字字符,% - 是字母字符)

在多个线程中使用 OpenMP。

这是一段代码,但我不确定它是否做对了:

int i, j, m, n;

const char alph[26] = "abcdefghijklmnopqrstuvwxyz";
const char num[10] = "0123456789";

#pragma omp parallel for private(pass) schedule(dynamic) collapse(4)
for (i = 0; i < 26; i++)
    for (j = 0; j < 26; j++)
        for (m = 0; m < 10; m++)
            for (n = 0; n < 10; n++) {
                pass[0] = alph[i];
                pass[1] = alph[j];
                pass[2] = num[m];
                pass[3] = num[n];

                /* Working with pass here */

            }

所以我的问题是
如何正确指定“parallel for”指令,以便在多个内核之间拆分密码范围?

非常感谢您的帮助。

【问题讨论】:

  • 嗯,在两个地方你有alph,你应该有num,这显然是错误的,但这与OpenMP没有任何关系。 OMP 编译指示本身在我对 OpenMP 的理解(有限)范围内似乎是正确的。请展示您的完整程序,并详细描述您希望它做什么以及它实际上做了什么。
  • 如果您将pass[0] = alph[i]; 等从最里面的循环移到相关循环的内部,这对您来说会更明显。
  • 这是我们要求Minimal, Complete, and Verifiable example 的原因之一。
  • @JoaoAlby 再次,请展示完整程序,并详细描述您希望它做什么以及它实际做了什么。
  • @WeatherVane 如果我理解正确,这不是collapse 关键字的工作方式。要使用它,所有外循环必须“完全嵌套”(外循环的循环体只能由内循环组成)。

标签: c multithreading parallel-processing openmp brute-force


【解决方案1】:

您的代码非常正确,除了使用alph 而不是num。如果您能够在循环中定义 pass 变量,那将省去很多麻烦。

完整的 MWE 可能如下所示:

//Compile with, e.g.: gcc -O3 temp.c -std=c99 -fopenmp
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int PassCheck(char *pass){
  usleep(50); //Sleep for 100 microseconds to simulate work
  return strncmp(pass, "qr34", 4)==0;
}

int main(){
  const char alph[27] = "abcdefghijklmnopqrstuvwxyz";
  const char num[11]  = "0123456789";
  char goodpass[5] = "----"; //Provide a default password to indicate an error state

  int i, j, m, n;

  #pragma omp parallel for collapse(4)
  for (i = 0; i < 26; i++)
  for (j = 0; j < 26; j++)
  for (m = 0; m < 10; m++)
  for (n = 0; n < 10; n++){
    char pass[4];
    pass[0] = alph[i];
    pass[1] = alph[j];
    pass[2] = num[m];
    pass[3] = num[n];
    if(PassCheck(pass)){
      //It is good practice to use `critical` here in case two
      //passwords are somehow both valid. This won't arise in
      //your code, but is worth thinking about.
      #pragma omp critical
      {
        memcpy(goodpass, pass, 4);
        goodpass[4] = '\0';
        //#pragma omp cancel for //Escape for loops!
      }
    }
  }

  printf("Password was '%s'.\n",goodpass);

  return 0;
}

动态调度

在这里使用dynamic 时间表可能毫无意义。您的期望应该是每个密码平均花费大约相同的时间来检查。因此,循环的每次迭代将花费大约相同的时间。因此,无需使用动态调度,因为您的循环将保持均匀分布。

视觉噪音

请注意,循环嵌套是堆叠的,而不是缩进的。你会经常在有许多嵌套循环的代码中看到这一点,因为它往往会减少视觉噪音。

提前休息

#pragma omp cancel for 从 OpenMP 4.0 开始可用;但是,我在这种情况下使用它时收到警告,因此我已将其注释掉。如果您能够使其正常工作,那么您的运行时间将减少一半,因为一旦找到正确的密码,所有的努力都将被浪费掉,而且密码平均会位于搜索空间的中途。

猜测密码的生成位置

其中一位评论者建议搬家,例如pass[0] 使其不在最内层循环中。这是一个坏主意,因为这样做会阻止您使用collapse(4)。因此,您可以并行化外循环,但您会冒着无法将其迭代次数除以线程数的风险,从而导致较大的负载不平衡。或者,您可以并行化内部循环,这会使您面临同样的问题,并且每次循环结束时都会产生高昂的同步成本。

为什么是usleep

usleep 函数导致代码运行缓慢。这是故意的;它提供了有关并行效果的反馈,因为工作负载非常小。

如果我删除usleep,那么代码在单核上完成时间为 0.003 秒,在四核上完成时间为 0.004 秒。您无法判断并行性是否有效。将usleep 保留在单核上为 8.950 秒,四核上为 2.257 秒,这很好地证明了并行性的有效性。

一旦您确定并行性正常工作,您自然会删除此行。

此外,任何实际的暴力破解密码破解者都可能在 PassCheck 函数内计算一个昂贵的哈希函数。在此处包含usleep() 允许我们模拟该功能并进行高级设计实验,而无需先使用该功能。

【讨论】:

  • 谢谢,这确实是我要找的。​​span>
  • @JoaoAlby:注意应该使用strncmp 而不是strcmp。使用-fsanitize=address 编译标志有助于识别此类错误。
  • 这是一个很好的答案。我受到它的启发来解决cancel for 问题。我推广到任意数量的数字(n 个嵌套循环),我可以取消嵌套循环。这是我的coliru.stacked-crooked.com/a/e585c176b12bc607
  • @Zboson:太棒了!您应该在代码中添加(一堆)cmets,解释各种位的推理并将其发布为答案:-)
猜你喜欢
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 1970-01-01
  • 2012-07-14
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 1970-01-01
相关资源
最近更新 更多