我试图将一段代码从Python移植到C。代码所做的是从纯文本字列表生成一个预先计算的MD5哈希列表。
在所有3种编程语言中,该代码在SHA-1和SHA-2家族中也有其变体。结构是一样的。
我最初是用Bash编写的代码,速度很慢。所以我把它移植到Python上,它的速度要快得多。现在,我已经成功地将代码移植到C上,希望它能运行得更快。但是,即使在gcc中使用-O更快的标志,代码的运行速度仍然比Python慢(执行时间的差异随着输入大小的增加而呈指数增长)。
我也对OpenSSL密码库的效率表示怀疑,但在我看来,在阅读了它们的文档之后,它们已经相对完善了。
我猜想是我在代码的C版本中实现的嵌套循环减缓了整个过程。
有什么建议来提高业绩吗?
Bash版本:
#/!bin/bash边读边做printf $line \ md5已完成
Python版本:
导入hashlib infile = 'wordlist‘outfile =open(“预算法”,"a")和open(infile,"r")作为inf: for行inf: outfile.write(hashlib.md5(line.strip().encode('utf8')).hexdigest()+'\n')
C版本:
//-----------Libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>
//------------------------------Main Function------------------------//
int main()
{
//------Define infile, outfile, file length. Define string to be read.---//
FILE *infile, *outfile;
char *string = NULL;
size_t len = 0;
ssize_t read;
//------Open File stream for read(r) and write (w). Error Handling.--//
//Part of MD5 Hash Function (Taken out of While Loop for Optimization)
int md5;
unsigned char result[MD5_DIGEST_LENGTH];
//
infile = fopen("file.txt", "r");
if (infile == NULL)
exit(EXIT_FAILURE);
outfile = fopen("MD.txt","w");
if (outfile == NULL)
exit(EXIT_FAILURE);
//-------------Read line-by-line in using a while loop.--------------//
while ((read = getline(&string, &len, infile)) != -1) {
string[strcspn(string, "\n")] = 0; // Remove newline '\n'
//-------------------------MD5 Hash Function-------------------------//
MD5(string, strlen(string), result);
//output
for(md5 = 0; md5 < MD5_DIGEST_LENGTH; md5++)
fprintf(outfile,"%02x",result[md5]); //convert the hash to hex
fprintf(outfile,"\n"); //newline for the output file
}
free(string); //free string
fclose(infile); // close file streams
fclose(outfile);
exit(EXIT_SUCCESS); //Program Ends
}如果需要,我可以提供不同大小的输入的执行时间。任何帮助都将不胜感激。
发布于 2018-04-17 20:01:46
在提出可能的性能改进之前,我将首先回顾一下您当前的代码。
代码中的各种注释不添加信息,可以删除,例如
//------------------------------Main Function------------------------//
int main()
free(string); //free string
exit(EXIT_SUCCESS); //Program Ends始终用大括号表示if -语句,即使if或{ }部分仅由单个语句组成。如果稍后编辑代码,这将有助于避免错误。
在使用变量的最窄范围内声明变量,而不是在函数顶部声明变量。例如
FILE *infile = fopen("file.txt", "r");
if (infile == NULL) {
exit(EXIT_FAILURE);
}
FILE *outfile = fopen("MD.txt","w");
if (outfile == NULL) {
exit(EXIT_FAILURE);
}或
for (int md5 = 0; md5 < MD5_DIGEST_LENGTH; md5++) { ... }在顶部声明md5不会提高性能。
一些变量名称可以改进(当然,这部分是基于意见的):
char *string实际上是当前行。size_t len不是当前字符串的长度,而是getline()分配的缓冲区(Re)的容量。ssize_t read不是读入缓冲区的字节数,因为它排除了NUL字符。int md5不是MD5值,而是包含MD5哈希的缓冲区的索引。unsigned char result[MD5_DIGEST_LENGTH]重命名为md5hash。修正编译器警告:
MD5(string, strlen(string), result);
// Passing 'char *' to parameter of type 'const unsigned char *' converts between pointers to integer types with different sign您已经使用了不同的退出代码来表示程序的成功或失败,这是很好的。此外,在错误情况下打印一些消息(到标准错误)也是有帮助的。
根据C11标准,main的声明应是
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }(例如,请参见堆栈溢出的在C和C++中main()应该返回什么? )。最后一个return语句可以省略,有一个隐式return 0。
从输入文件中读取一行后,将遍历该字符串两次:
while ((read = getline(&string, &len, infile)) != -1) {
string[strcspn(string, "\n")] = 0; // Remove newline '\n'
MD5(string, strlen(string), result);
// ...
}首先查找终止换行符,然后再确定长度。这是不必要的,因为getline()返回写入string的字符数,即换行符只能位于read - 1位置:
while ((read = getline(&string, &len, infile)) != -1) {
// Remove trailing newline character
if (read > 0 && string[read - 1] == '\n') {
read -= 1;
string[read] = 0;
}
MD5(string, read, result);
// ...
}然而,这一变化的影响取决于线的长度,我无法观察到在我的测试显着的差异。
为了找出进一步的性能瓶颈,我现在用Xcode/仪器(使用./crunch 7 7 1234567890生成的输入文件)对程序进行了分析。
这立即表明,大部分时间是在fprintf()上度过的:

可能的原因是:
解决办法是:
printf,从而减少函数调用的次数。在我的计算机( 1.2 GHz Intel Core m5 MacBook)上,这将处理上述文件的时间从24.5秒缩短到4.4秒。
经过所有这些修改,我们
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>
// Format the data as a hexadecimal string. The buffer must have
// space for `2 * length + 1` characters.
const char *hexString(unsigned char *data, size_t length, char *buffer) {
const char *hexDigits = "0123456789abcdef";
char *dest = buffer;
for (size_t i = 0; i < length; i++) {
*dest++ = hexDigits[data[i] >> 4];
*dest++ = hexDigits[data[i] & 0x0F];
}
*dest = 0;
return buffer;
}
int main(void) {
FILE *infile = fopen("file.txt", "r");
if (infile == NULL) {
perror("Cannot open input file");
exit(EXIT_FAILURE);
}
FILE *outfile = fopen("MD.txt","w");
if (outfile == NULL) {
perror("Cannot open output file");
exit(EXIT_FAILURE);
}
// Read file line-by-line
char *line = NULL;
size_t linecap = 0;
ssize_t lineLength;
while ((lineLength = getline(&line, &linecap, infile)) != -1) {
if (lineLength > 0 && line[lineLength - 1] == '\n') {
// Remove newline character
lineLength -= 1;
line[lineLength] = 0;
}
// Compute MD5 hash
unsigned char md5hash[MD5_DIGEST_LENGTH];
MD5((unsigned char*)line, lineLength, md5hash);
// Print hash as hex string
char hexBuffer[2 * MD5_DIGEST_LENGTH + 1];
fputs(hexString(md5hash, MD5_DIGEST_LENGTH, hexBuffer), outfile);
fputc('\n', outfile);
}
free(line);
// Close output files
fclose(infile);
fclose(outfile);
}-h选项以显示程序的简短帮助/使用情况。https://codereview.stackexchange.com/questions/192306
复制相似问题