2011-04-28 63 views
1

我必须从Web服务器的htdocs目录中获取大量文件名,然后使用此文件名列表来搜索大量归档日志文件,以便在这些文件上进行上次访问。在巨大的日志文件中搜索数百种模式

我打算在C++中使用Boost来做到这一点。我会先取最新的日志,然后逐行阅读,以检查每一行的所有文件名。

如果文件名匹配,我读取日志串中的时间并保存它的最后访问权限。现在我不需要再查找这个文件了,因为我只想知道上次访问。

要搜索的文件名向量应该快速减少。

我想知道如何处理这种多线程最有效的问题。

我是否对日志文件进行分区并让每个线程都从内存中搜索日志的一部分,并且如果某个线程有匹配项,它将从文件名向量中删除该文件名,或者是否有更有效的方法来执行此操作?

+1

这可能是一个IO界限操作,而不是任何事情,所以我不认为线程会在这里帮助任何事情。 – 2011-04-29 00:07:24

+0

如果你有Mathematica,你可以写一些东西来做到这一点很快。 – 2011-04-29 00:29:51

+0

难道你不认为只是将日志文件读入内存,并将字符串拆分为4或8个线程以便同时从内存中搜索它们会提高性能吗?这将是类似Map Reduce的方法,其中使用多个线程来完成内存映射数据的工作,但是您有分离的阶段来读取和处理数据。这样你就不必处理任何同步。 – netsky 2011-04-29 00:29:58

回答

1

尝试使用mmap,它会为您节省相当多的脱发。我感到快速,并以某种奇怪的心情回忆我的mmap知识,所以我写了一个简单的事情来让你开始。希望这可以帮助!

mmap的美妙之处在于它可以很容易地与OpenMP并行化。这也是预防I/O瓶颈的一个好方法。我首先定义Logfile类,然后再继续实现。

这里的头文件(logfile.h)

#ifndef _LOGFILE_H_ 
#define _LOGFILE_H_ 

#include <iostream> 
#include <fcntl.h> 
#include <stdio.h> 
#include <string> 
#include <sys/mman.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 

using std::string; 

class Logfile { 

public: 

    Logfile(string title); 

    char* open(); 
    unsigned int get_size() const; 
    string get_name() const; 
    bool close(); 

private: 

    string name; 
    char* start; 
    unsigned int size; 
    int file_descriptor; 

}; 

#endif 

而这里的.cpp文件。现在

#include <iostream> 
#include "logfile.h" 

using namespace std; 

Logfile::Logfile(string name){ 
    this->name = name; 
    start = NULL; 
    size = 0; 
    file_descriptor = -1; 

} 

char* Logfile::open(){ 

    // get file size 
    struct stat st; 
    stat(title.c_str(), &st); 

    size = st.st_size; 

    // get file descriptor 
    file_descriptor = open(title.c_str(), O_RDONLY); 
    if(file_descriptor < 0){ 
     cerr << "Error obtaining file descriptor for: " << title.c_str() << endl; 
     return NULL; 
    } 

    // memory map part 
    start = (char*) mmap(NULL, size, PROT_READ, MAP_SHARED, file_descriptor, 0); 
    if(start == NULL){ 
     cerr << "Error memory-mapping the file\n"; 
     close(file_descriptor); 
     return NULL; 
    } 

    return start; 
} 

unsigned int Logfile::get_size() const { 
    return size; 
} 

string Logfile::get_title() const { 
    return title; 
} 

bool Logfile::close(){ 

    if(start == NULL){ 
     cerr << "Error closing file. Was closetext() called without a matching opentext() ?\n"; 
     return false; 
    } 

    // unmap memory and close file 
    bool ret = munmap(start, size) != -1 && close(file_descriptor) != -1; 
    start = NULL; 
    return ret; 

} 

,使用此代码,你可以使用OpenMP的工作份额,这些日志文件的解析,即

Logfile lf ("yourfile"); 
char * log = lf.open(); 
int size = (int) lf.get_size(); 

#pragma omp parallel shared(log, size) private(i) 
{ 
    #pragma omp for 
    for (i = 0 ; i < size ; i++) { 
    // do your routine 
    } 
    #pragma omp critical 
    // some methods that combine the thread results 
} 
+0

这看起来像我想要做的。所以在将数据映射到内存之后,omp会为我设置循环。这为我节省了很多工作,正是我所期待的,非常感谢! – netsky 2011-04-29 00:40:04

+0

没问题,很高兴我有帮助! – 2011-04-29 03:36:15

+0

只是想我会补充说,你应该将size变量设置为firstprivate(size),而不是共享,以避免循环中的数据竞争条件。 – 2011-05-06 05:33:43

1

将日志文件解析为数据库表(SQLite ftw)。其中一个领域将是路径。

在另一个表中,添加您正在查找的文件。

现在,它是派生表上的简单联接。像这样的东西。

SELECT l.file, l.last_access FROM toFind f 
LEFT JOIN ( 
    SELECT file, max(last_access) as last_access from logs group by file 
) as l ON f.file = l.file 

toFind中的所有文件都将存在,并且对于日志中未找到的文件将具有last_access NULL。

+0

这真的很快吗? – netsky 2011-04-29 00:31:27

+0

是的。您需要添加一个索引到l.file和f.file。如果您需要,您还可以进行进一步的数据处理。进口将是最慢的部分,但正如乔伊所说,无论如何,这将是IO界限。 – 2011-04-29 02:37:16

0

确定这是前几天已经但是我花了一些时间编写代码和在其他项目中使用SQLite。

我仍然想将DB-Approach与MMAP解决方案进行比较,仅仅为了性能方面。

如果您可以使用SQL查询来处理您解析的所有数据,它当然可以为您节省大量工作。但我真的不关心工作量,因为我仍然学到很多东西,从中我学到的是:

这种MMAP方法 - 如果您正确实施它 - 在性能上绝对优越。如果您实施可被视为MapReduce Algo的“hello world”的“字数统计”示例,您会注意到它的速度令人难以置信。

现在,如果您希望进一步从SQL语言中受益,正确的方法是使用在线程间共享查询的方式实现您自己的使用Map-Reduce的SQL-Wrapper。

你也许可以通过线程间的ID来共享对象,其中每个线程处理它自己的DB-Connection。然后它会在它自己的数据集中查询对象。

这比用SQLite DB写常规方式要快得多。

毕竟你可以说:

MMAP是处理字符串处理 SQL的语法分析器的应用程序提供了强大的功能以最快的方式,但如果你不执行处理SQL查询的包装它慢下来的东西