【问题标题】:Segfault after adding #pragma loop添加#pragma循环后的段错误
【发布时间】:2018-06-01 13:12:47
【问题描述】:

我遇到了一些麻烦。我不确定我是否能理解。 所以,我有一些代码。我正在尝试为代码中的几个循环添加#pragma loop(hint_parallel(8)) 语句。 当我使用必要的编译选项编译它时,实际上是这样的:

gcc -w -funroll-loops -O2 -fno-inline -fipa-pta -msse2 -funsafe-math-optimizations -ftree-vectorizer-verbose=1 -fopt-info-optimized=logs/optOpt.txt -shared -fPIC singleThread.cpp

我得到分段错误。

fish: './a.out' 被信号 SIGSEGV 终止(地址边界错误)

关键是我不知道为什么会这样。我怀疑这可能是这些循环中使用的常量的问题。但我不认为这是相关的。如果我只是使用 -O0 优化编译这段代码,它就可以正常工作(因为编译器不会向量化我猜的东西)。

请您看看下面的代码,并建议我应该检查哪个方向。 谢谢。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
#include <cstdio>
#include <set>
#include <fstream>
#include <cstdint>
#include <climits>

using namespace std;

const int STRING_HASH_SIZE = 32;

int convert(vector<string> &inputVector, const char **outputArray);

void printCollisions(const char **charArray, int size);

void printArray(const char **arrayToPrint, int size);

int getHashCode(const char *characters, unsigned long size);

string getRandomString();

void writeFileIfNeeded(vector<string> &vector, bool needToWrite);

vector<string> generateStringsVector(int size, bool isNeedToWriteFile);

/**
 * main method is present to test these native code.
 * to perform some external operation we should use another method.
 * @return
 */
int main() {

    /**
     * The constant represents number of strings that will be generated
     * in the string vector generation.
    */
    const int STRING_NUMBERS = 100000;

    vector<string> inputVector = generateStringsVector(STRING_NUMBERS, false);

#pragma pack 8
    const char *charArray[inputVector.size()];

    int hashResult = convert(inputVector, charArray);

    if (hashResult != 0) {
        return 0;
    }

    printCollisions(charArray, STRING_NUMBERS);
}

/**
 * Converts an input vector to char array.
 * Getting a hash of
 * Returns 0 if conversion from vector to array has been successfully performed.
 * @param  inputVector [ input array reference ]
 * @param outputArray [ a char array that would contain char sequences from vector ]
 * @return           [ hash sum (int)]
 */
int convert(vector<string> &inputVector, const char **outputArray) {
    int hashSum = 0;
#pragma loop(hint_parallel(8))
    for (int i = 0; i < inputVector.size(); i++) {
        outputArray[i] = inputVector[i].c_str();
    }

#pragma loop(hint_parallel(8))
    for (auto &i : inputVector) {
        hashSum += getHashCode(i.c_str(), i.length());
    }

    int stringHashSize = STRING_HASH_SIZE;
#pragma loop(hint_parallel(8))
    for (int i = 0; i < inputVector.size(); i++) {
        hashSum -= getHashCode(outputArray[i], stringHashSize);
    }

    if (hashSum != 0) {
        cout << "\nConversion isn't succeeded, hash = " << hashSum << endl;
    } else {
        cout << "\nConversion succeeded" << endl;
    }
    return hashSum;
}

/**
 * Prints count and percentage of collisions in array hash codes
 * @param charArray
 * @param size
 */
void printCollisions(const char **charArray, int size) {
    set<int> setOfHashes;
    int stringHashSize = STRING_HASH_SIZE;
#pragma loop(hint_parallel(8))
    for (int i = 0; i < size; i++) {
        setOfHashes.insert(getHashCode(charArray[i], stringHashSize));
    }
    unsigned long collisions = size - setOfHashes.size();
    cout << collisions << "/" << size << " " << 100.0 * collisions / size << "% of collisions";
}

/**
 * Prints input char array
 * @param arrayToPrint
 */
void printArray(const char **arrayToPrint, int size) {
    cout << "\nPrinted array size = " << size << endl;
    for (int i = 0; i < size; i++) {
        cout << arrayToPrint[i] << ":" << getHashCode(arrayToPrint[i], STRING_HASH_SIZE) << endl;
    }
}

/**
 *
 * @param characters
 * @return
 */
int getHashCode(const char *characters, unsigned long size) {
    int hash = 0;
#pragma loop(hint_parallel(8))
    for (int i = 0; i < size; i++) {
        hash = (31 + hash) * (characters[i]);
    }
    return hash;
}

/**
 * Get a random String from alphabetical char sequence.
 * @return a randomized string according to an alphabet.
 */
string getRandomString() {
    string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
    random_device rd;
    mt19937 generator(rd());
    shuffle(str.begin(), str.end(), generator);
    return str.substr(0, STRING_HASH_SIZE);
}

/**
 * Generates a vector with random strings
 * @param size - an int value that will be used as size of a generated vector
 * @return reference to generated vector.
 */
vector<string> generateStringsVector(int size, bool isNeedToWriteFile) {
    vector<string> charArray;
#pragma loop(hint_parallel(8))
    for (int i = 0; i < size; i++) {
        string str = getRandomString();
        charArray.push_back(str);
    }
    writeFileIfNeeded(charArray, isNeedToWriteFile);
    return charArray;
}

/**
 * Writes file with name according to vector size (e.g. 100000.csv)
 * if needToWrite is true
 * @param vector
 * @param needToWrite
 */
void writeFileIfNeeded(vector<string> &vector, bool needToWrite) {
    if (needToWrite) {
        ofstream csvFile;
        string filename = to_string(vector.size()) + ".csv";

        csvFile.open(filename, fstream::out);
        for (const auto &i : vector) {
            csvFile << i << "\n";
        }
        csvFile.close();
    }
}

【问题讨论】:

  • @andreee 谢谢你的回答。是的,Knuth 说的是实话 :) 我同意计算哈希和,我没有考虑过。但是转换函数的第一个循环呢?这不是reduce操作,是吗?实际上,我没有考虑过 OpenMP。但我也会尝试。 OpenMP 适用于 Linux 吗?也许如果我删除不必要的#pragmas 它会起作用。

标签: c++ arrays segmentation-fault


【解决方案1】:

导致分段错误的原因是您编译代码的方式,而不是编译指示(无论如何它在 gcc 中没有任何影响,见下文):

gcc -w -funroll-loops -O2 -fno-inline -fipa-pta -msse2 -funsafe-math-optimizations -ftree-vectorizer-verbose=1 -fopt-info-optimized=logs/optOpt.txt -shared -fPIC singleThread.cpp

  • 通过使用-shared -fPIC,您正在创建一个 DSO(动态共享对象)。如果你试图执行这个文件,你会得到一个无效的 PC(程序计数器)并且你的程序会立即崩溃。你必须编译你的代码没有-shared -fPIC(如果你需要一个与位置无关的可执行文件,请使用-pie -fPIE)。
  • 此外,对于编译 C++ 代码,您通常应该使用 g++ 而不是 gcc
  • 给定的编译指示不应该对您的代码产生任何影响,因为这些编译指示只有 Microsoft Visual Studio 才能理解。将-Wall 添加到您的编译选项中,gcc 将向您显示相应的警告。
  • 在任何情况下,您都应该摆脱供应商特定的编译指示,而改用 OpenMP 等标准化解决方案(使用 -fopenmp 编译)。这样,您就离编写独立于编译器的代码更近了一步。
  • 对于并行循环,您应该确保不会遇到竞争条件或其他同步失败。例如,要计算总和,#pragma omp parallel for reduction(+: sum) 是您在 OpenMP 中的朋友 (reference sheet)。

免责声明:我在 x86_64 (CentOS Linux) 上使用过 gcc 7.3.0。

【讨论】:

  • 感谢您的回答。这很有帮助。当我使用 -openmp 选项编译时,我是否应该使用示例中的其他选项?还是 -openmp 选项就够了?
  • -fopenmp 就够了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多