1
\$\begingroup\$

I've written my first C++ multithreaded application (as far as I can recall) which determines if a regex can be found in a string.

This code works correct to the best of my knowledge.

Could you gals/guys tell me what I could improve on this code?

main.cpp (one file):

#include <iostream>
#include <chrono>
using namespace std::chrono;

template<typename T>
auto toMs(T &&time) {
    return duration_cast<milliseconds>(time).count();
}

#include <string>
#include <regex>
void cxxregex(std::string const&inputString, std::string const&searchRegex)
{
    auto startTime = steady_clock::now();
    std::regex regexObj(searchRegex.data(), std::regex::extended);
    std::smatch matchObj;
    regex_search(inputString.begin(), inputString.end(), matchObj, regexObj);
    std::cout << toMs(steady_clock::now() - startTime) << "ms " << matchObj[1] << std::endl;
}

#include <boost/regex.hpp>
void boostregex(std::string const&inputString, std::string const&searchRegex)
{
    auto startTime = steady_clock::now();
    boost::regex regexObj(searchRegex.data(), boost::regex::extended);
    boost::smatch matchObj;
    regex_search(inputString.begin(), inputString.end(), matchObj, regexObj);
    std::cout << toMs(steady_clock::now() - startTime) << "ms " << matchObj[1] << std::endl;
}

#include <future>
void threadFcn(
    const int threadId,
    const std::string::const_iterator& subStringBegin,
    const std::string::const_iterator& subStringEnd,
    const boost::regex& regexObj,
    std::promise<std::pair<size_t, std::string>>& matchProm,
    const std::shared_future<std::pair<size_t, std::string>>& matchFut)
{
    boost::smatch matchObj;
    if (regex_search(subStringBegin, subStringEnd, matchObj, regexObj))
    {
        // match is found!
        if (matchFut.wait_for(0s) != std::future_status::ready) // check of not already set
            matchProm.set_value(std::make_pair(threadId, matchObj[1]));// matchObj.str()));
    }
}

#include <thread>
#include <vector>
#include <algorithm>
using namespace std::chrono_literals;
void boostparregex(std::string const &inputString, std::string const &searchRegex)
{
    auto nrOfCpus = std::thread::hardware_concurrency() / 2;
    std::cout << "(Nr of CPUs: " << nrOfCpus << ") ";

    if (searchRegex.find("^") != std::string::npos || searchRegex.find("$") != std::string::npos) {
    //if (boost::regex_match(searchRegex, boost::regex("(.*\\^.*)|(.*\\$.*)"))) {
        std::cout << "Start-of-line not supported multi-CPU. Defaulting to 1.\n";
        nrOfCpus = 1;
    }

    auto startTime = steady_clock::now();
    boost::regex regexObj(searchRegex.data(), boost::regex::extended);

    std::promise<std::pair<size_t, std::string>> foundThreadIdProm;
    auto foundThreadIdFut = foundThreadIdProm.get_future().share();
    std::vector<std::thread> threadVector; threadVector.reserve(nrOfCpus);
    auto stringSizePerThread = inputString.length() / nrOfCpus;
    for (size_t threadId = 0; threadId < nrOfCpus; threadId++) {
        // determine string start and end
        const auto begin = inputString.begin() + (threadId * stringSizePerThread);
        const auto end = inputString.end(); // all cpu's search to end of input string. they only differ in start
        // create thread
        threadVector.push_back(std::thread(threadFcn,
            (int)threadId, std::ref(begin), std::ref(end), std::ref(regexObj), std::ref(foundThreadIdProm), std::ref(foundThreadIdFut)));
    }

    if (foundThreadIdFut.wait_for(10s) == std::future_status::ready) std::cout
        << toMs(steady_clock::now() - startTime) << "ms. "
        << "Thread " << foundThreadIdFut.get().first << ": " << foundThreadIdFut.get().second << std::endl;
    else std::cerr << "Error: string not found!!" << std::endl;

    for (auto& thread : threadVector) thread.join(); // I'd rather detach... but then the references are destroyed, giving problems.
}

int main()
{
    std::string s(1000000000, 'x');
    {
        std::string s1 = "yolo" + s;
        std::cout << "yolo + ... -> cxxregex "; cxxregex(s1, "(yolo)");
        std::cout << "yolo + ... -> boostregex "; boostregex(s1, "(yolo)");
        std::cout << "yolo + ... -> boostparregex "; boostparregex(s1, "(yolo)");
    }
    {
        std::string s2 = s + "yolo";
        std::cout << "... + yolo -> cxxregex "; cxxregex(s2, "(yolo)");
        std::cout << "... + yolo -> boostregex "; boostregex(s2, "(yolo)");
        std::cout << "... + yolo -> boostparregex "; boostparregex(s2, "(yolo)");
    }
    {
        std::string s3 = "yo" + s + "lo";
        //std::cout << "... + yolo -> cxxregex "; cxxregex(s3, "(yo).*(lo)"); // doesn't even work
        std::cout << "... + yolo -> boostregex "; boostregex(s3, "(yo).*(lo)");
        std::cout << "... + yolo -> boostparregex "; boostparregex(s3, "(yo).*(lo)");
    }

    std::cin.ignore();
}

Typical output:

yolo + ... -> cxxregex 0ms yolo
yolo + ... -> boostregex 1ms yolo
yolo + ... -> boostparregex (Nr of CPUs: 4) 14ms. Thread 0: yolo
... + yolo -> cxxregex 5214ms yolo
... + yolo -> boostregex 779ms yolo
... + yolo -> boostparregex (Nr of CPUs: 4) 251ms. Thread 3: yolo
... + yolo -> boostregex 1636ms yo
... + yolo -> boostparregex (Nr of CPUs: 4) 1706ms. Thread 0: yo
\$\endgroup\$
  • 2
    \$\begingroup\$ Is this all a single file? Or several ones? \$\endgroup\$ – Zeta Jun 27 '18 at 12:27
  • \$\begingroup\$ @Zeta It is not fully clear to me what you mean. What I'm showing here is the parallel algorithm and a demonstration main to show the speedup over standard std/boost regex. Do you want me to split the code in a part I want feedback on and the test part? \$\endgroup\$ – JHBonarius Jun 27 '18 at 15:10
  • \$\begingroup\$ It's not clear whether all your code is in a single file in your project. And there will be at least one comment on that. At the moment, the #include <…> headers look like you cat several source files. \$\endgroup\$ – Zeta Jun 27 '18 at 16:19
  • \$\begingroup\$ @Zeta This is one single main.cpp. Compilers don't seem to have an issue with me dividing includes over the file, so I include things where I need them. I would add a live example, but no online compiler seems to offer multiple threads to demonstrate. \$\endgroup\$ – JHBonarius Jun 27 '18 at 17:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.