2012-11-12 14 views
7

मैं बूस्ट स्पिरिट क्यूआई के साथ टीपीसीएच फाइलों को पार्स करने का प्रयास करता हूं। मेरा कार्यान्वयन आत्मा क्यूआई (http://www.boost.org/doc/libs/1_52_0/libs/spirit/example/qi/employee.cpp) के कर्मचारी उदाहरण से प्रेरित है। डेटा सीएसवी प्रारूप में है और टोकन को '|' से सीमित किया गया है चरित्र।बूस्ट स्पिरिट क्यूआई धीमी

यह काम करता है लेकिन यह बहुत धीमा है (1 जीबी के लिए 20 सेकंड)।

struct lineitem { 
    int l_orderkey; 
    int l_partkey; 
    int l_suppkey; 
    int l_linenumber; 
    std::string l_quantity; 
    std::string l_extendedprice; 
    std::string l_discount; 
    std::string l_tax; 
    std::string l_returnflag; 
    std::string l_linestatus; 
    std::string l_shipdate; 
    std::string l_commitdate; 
    std::string l_recepitdate; 
    std::string l_shipinstruct; 
    std::string l_shipmode; 
    std::string l_comment; 
}; 

BOOST_FUSION_ADAPT_STRUCT(lineitem, 
    (int, l_orderkey) 
    (int, l_partkey) 
    (int, l_suppkey) 
    (int, l_linenumber) 
    (std::string, l_quantity) 
    (std::string, l_extendedprice) 
    (std::string, l_discount) 
    (std::string, l_tax) 
    (std::string, l_returnflag) 
    (std::string, l_linestatus) 
    (std::string, l_shipdate) 
    (std::string, l_commitdate) 
    (std::string, l_recepitdate) 
    (std::string, l_shipinstruct) 
    (std::string, l_shipmode) 
    (std::string, l_comment)) 

vector<lineitem>* lineitems=new vector<lineitem>(); 

phrase_parse(state->dataPointer, 
    state->dataEndPointer, 
    (*(int_ >> "|" >> 
    int_ >> "|" >> 
    int_ >> "|" >> 
    int_ >> "|" >> 
    +(char_ - '|') >> "|" >> 
    +(char_ - '|') >> "|" >> 
    +(char_ - '|') >> "|" >> 
    +(char_ - '|') >> "|" >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' >> 
    +(char_ - '|') >> '|' 
    )), space, *lineitems 
); 

समस्या चरित्र पार्स हो रहा है:

यहाँ पंक्ति आइटम फ़ाइल के लिए मेरे क्यूई व्याकरण है। यह अन्य रूपांतरणों की तुलना में बहुत धीमी है। तारों में परिवर्तनीय लंबाई टोकन पार्स करने का कोई बेहतर तरीका है?

+0

मुझे एक बार ऐसा अनुभव हुआ। आत्मा क्यूई परिवर्तनीय लंबाई तारों को कुशलता से संभालने में सक्षम नहीं है। किसी के पास इसका समाधान है? – muehlbau

उत्तर

5

मैं अपने समस्या का समाधान मिल गया:

एक अन्य संभावित समाधान दोहराव पार्सर निर्देशक का उपयोग करें। जैसा कि इस पोस्ट में वर्णित है Boost Spirit QI grammar slow for parsing delimited strings प्रदर्शन बाधा आत्मा क्यूई की स्ट्रिंग हैंडलिंग है। अन्य सभी डेटा प्रकार काफी तेजी से प्रतीत होते हैं।

मैं आत्मा क्यूई हैंडलिंग का उपयोग करने के बजाय अपने स्वयं के डेटा को संभालने के माध्यम से इस समस्या से बचता हूं।

मेरा समाधान एक सहायक वर्ग का उपयोग करता है जो सीएसवी फ़ाइल के हर क्षेत्र के लिए कार्य प्रदान करता है। कार्य मूल्यों को एक संरचना में संग्रहीत करते हैं। स्ट्रिंग्स को char [] s में संग्रहीत किया जाता है। पार्सर को एक न्यूलाइन चरित्र हिट करता है, यह एक फ़ंक्शन को कॉल करता है जो परिणाम वेक्टर में संरचना जोड़ता है। बूस्ट पार्सर अपने कार्यों को वेक्टर में मूल्यों को संग्रहीत करने के बजाय इस फ़ंक्शन को कॉल करता है।

यहाँ TCPH बेंचमार्क की region.tbl फ़ाइल के लिए अपने कोड है:

struct region{ 
    int r_regionkey; 
    char r_name[25]; 
    char r_comment[152]; 
}; 

class regionStorage{ 
public: 
regionStorage(vector<region>* regions) :regions(regions), pos(0) {} 
void storer_regionkey(int const&i){ 
    currentregion.r_regionkey = i; 
} 

void storer_name(char const&i){ 
    currentregion.r_name[pos] = i; 
    pos++; 
} 

void storer_comment(char const&i){ 
    currentregion.r_comment[pos] = i; 
    pos++; 
} 

void resetPos() { 
    pos = 0; 
} 

void endOfLine() { 
    pos = 0; 
    regions->push_back(currentregion); 
} 

private: 
vector<region>* regions; 
region currentregion; 
int pos; 
}; 


void parseRegion(){ 

    vector<region> regions; 
    regionStorage regionstorageObject(&regions); 
    phrase_parse(dataPointer, /*< start iterator >*/  
    state->dataEndPointer, /*< end iterator >*/ 
    (*(lexeme[ 
    +(int_[boost::bind(&regionStorage::storer_regionkey, &regionstorageObject, _1)] - '|') >> '|' >> 
    +(char_[boost::bind(&regionStorage::storer_name, &regionstorageObject, _1)] - '|') >> char_('|')[boost::bind(&regionStorage::resetPos, &regionstorageObject)] >> 
    +(char_[boost::bind(&regionStorage::storer_comment, &regionstorageObject, _1)] - '|') >> char_('|')[boost::bind(&regionStorage::endOfLine, &regionstorageObject)] 
    ])), space); 

    cout << regions.size() << endl; 
} 

यह एक सुंदर समाधान नहीं है, लेकिन यह काम करता है और यह बहुत तेजी से है। (1 जीबी टीसीपीएच डेटा के लिए 2.2 सेकेंड, मल्टीथ्रेडेड)

0

क्या आप संकलन करते समय -ओ 2 का उपयोग कर रहे हैं?

बूस्ट पुस्तकालयों में बहुत सी अनावश्यकता है जो अनुकूलन झंडे का उपयोग करते समय हटा दी जाती है। http://www.boost.org/doc/libs/1_52_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html

3

समस्या मुख्य रूप से char तत्वों को std::string कंटेनर में जोड़ने से आता है। आपके व्याकरण के अनुसार, प्रत्येक std::string विशेषता के लिए आवंटन शुरू होता है जब कोई char मिल जाता है और जब आपको | विभाजक मिलता है तो रोकता है। तो, पहले sizeof(char)+1 आरक्षित बाइट्स (शून्य-समाप्त "\ 0") हैं। संकलक को एल्गोरिदम दोगुनी आवंटकों के आधार पर std::string के आवंटन को चलाने होंगे! इसका मतलब है कि स्मृति को छोटे तारों के लिए अक्सर आवंटित किया जाना चाहिए। इसका मतलब है कि आपकी स्ट्रिंग को स्मृति आवंटन में दो बार आकार दिया गया है और पिछले आवंटन को 1,2,4,6,12,24 ... अंतराल के अंतराल पर मुक्त किया गया है। कोई आश्चर्य नहीं कि यह धीमा था, इससे लगातार मॉलोक कॉल के साथ बड़ी समस्याएं होती हैं; अधिक ढेर विखंडन, मुक्त मेमोरी ब्लॉक की एक बड़ी लिंक्ड सूची, उन मेमोरी ब्लॉक के परिवर्तनीय (छोटे) आकार जो इसके बदले में पूरे जीवनकाल के दौरान एप्लिकेशन के आवंटन के लिए स्मृति की लंबी स्कैनिंग के साथ समस्याएं पैदा करती हैं। tldr; डेटा खंडित हो जाता है और स्मृति में व्यापक रूप से फैल जाता है।

सबूत? निम्नलिखित कोड को char_parser द्वारा प्रत्येक बार आपके इटरेटर में मान्य वर्ण मिलने के लिए बुलाया जाता है। बूस्ट 1 से।54

/boost/spirit/home/qi/char/char_parser.hpp

if (first != last && this->derived().test(*first, context)) 
{ 
    spirit::traits::assign_to(*first, attr_); 
    ++first; 
    return true; 
} 
return false; 

/boost/spirit/home/qi/detail/assign_to.hpp

// T is not a container and not a string 
template <typename T_> 
static void call(T_ const& val, Attribute& attr, mpl::false_, mpl::false_) 
{ 
    traits::push_back(attr, val); 
} 

/बढ़ावा/आत्मा/घर/support/container.hpp

template <typename Container, typename T, typename Enable/* = void*/> 
struct push_back_container 
{ 
    static bool call(Container& c, T const& val) 
    { 
     c.insert(c.end(), val); 
     return true; 
    } 
}; 

सुधार अनुवर्ती कोड आप पोस्ट (अपने struct बदलते Name[Size] चार करने के लिए) मैं मूल रूप से एक स्ट्रिंग Name.reserve(Size) कथन निर्देश जोड़ने के समान है। हालांकि, इस समय इस के लिए कोई निर्देश नहीं है।

समाधान:

/boost/spirit/home/support/container.hpp

template <typename Container, typename T, typename Enable/* = void*/> 
struct push_back_container 
{ 
    static bool call(Container& c, T const& val, size_t initial_size = 8) 
    { 
     if (c.capacity() < initial_size) 
      c.reserve(initial_size); 
     c.insert(c.end(), val); 
     return true; 
    } 
}; 

/boost/spirit/home/qi/char/char_parser.hpp

if (first != last && this->derived().test(*first, context)) 
{ 
    spirit::traits::assign_to(*first, attr_); 
    ++first; 
    return true; 
} 
if (traits::is_container<Attribute>::value == true) 
    attr_.shrink_to_fit(); 
return false; 

मैंने इसका परीक्षण नहीं किया है, लेकिन मुझे लगता है कि यह आपके द्वारा देखा गया 10x से अधिक स्ट्रिंग विशेषताओं पर चार पार्सर्स को तेज कर सकता है। बूस्ट स्पिरिट अपडेट में यह एक महान अनुकूलन सुविधा होगी, जिसमें reserve(initial_size)[ +(char_ - lit("|")) ] निर्देश शामिल है जो प्रारंभिक बफर आकार सेट करता है।

संबंधित मुद्दे