2011-04-28 81 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查詢的包裝它慢下來的東西