Cpp - jgoffeney/Cesium4Unreal GitHub Wiki

Back

Multithreading

C++11

Threads were introduced in C++11. When creating the thread you pass in a static function or lambda, start processing and then use join() to wait for completion. Calling detach() after creation will separate the execution from the thread object.

std::vector<std::thread> threadList(numThreads);

// Create and start threads running the function
for(unsigned int tIdx = 0; tIdx < threadList.size(); tIdx++)
{
  threadList[tIdx] = std::thread(function, param0, param1, ..., paramN);
}

// Wait for threads to complete
for(unsigned int tIdx = 0; tIdx < threadList.size(); tIdx++)
{
  threadList[tIdx].join();
}

C++17

Since C++17 the C++ spec has included definitions for parallel processing.

Policies

Included in std::execution, policies are passed to functions to indicate how the data should be processed. It is designed to be extendable to target different architectures.

  • sequenced_policy seq : forces an algorithm to be run sequentially (not in parallel)
  • parallel_policy par : indicates an algorithm can be run in the calling thread or in a separate thread created by the library for parallel execution
  • parallel_unsequenced_policy par_unseq : indicates an algorithm may be parallelized, vectorized or migrated across threads. Element accesses are unsequenced within and across threads.
  • unsequenced_policy unseq : indicates an algorithm can be run on the same thread but vectorized via special instructions

Transform

The std::transform function can be used as a basis for running parallel algorithms.

Define a base function. This is defined as static.

std::vector<float> inputVector(10000, 2.0);  

std::vector<float> outputVector(10000);

// Double the value in the input
std::transform(std::execution::par, inputVector.begin(), inputVector.end(), 
  outputVector.begin(), [](float v){return v * 2.0}
);
Examples
glm::dvec3 GeospatialFunctions::_convertXYZToLLAd(const glm::mat4& transformMatrix,
    const glm::vec3& xyzPosition) {
    try {
        glm::dvec3 llaPosition;
        glm::dvec4 xyzwPosition(xyzPosition, 1.0);
        glm::dvec4 updatedXyz = LinearAlgebraFunctions::MatrixVectorMultiply(transformMatrix,
            xyzwPosition);

        const double a = 6378137.0;            // WGS 84 ellipsoid semi-major axis
        const double b = 6356752.3142451793f;   // WGS 84 ellipsoid semi-minor axis
        const double ecc1Sqrd = 0.00669438f;    // WGS 84 ellipsoid first eccentricity squared 
        const double ecc2Sqrd = 0.006739497f;   // WGS 84 ellipsoid second eccentricity squared 

        llaPosition.x = atan2(updatedXyz.y, updatedXyz.x) * RAD2DEG;

        double p = sqrt(updatedXyz.x * updatedXyz.x + updatedXyz.y * updatedXyz.y);
        double theta = atan2(updatedXyz.z * a, p * b);
        double sinTheta = sin(theta);
        double cosTheta = cos(theta);
        double latY = updatedXyz.z + ecc2Sqrd * b * sinTheta * sinTheta * sinTheta;
        double latX = p - ecc1Sqrd * a * cosTheta * cosTheta * cosTheta;

        //Compute latitude in radians
        double latRad = atan2(latY, latX);
        llaPosition.y = latRad * RAD2DEG;
        double sinLat = sin(latRad);

        double N = a / sqrt(1.0f - ecc1Sqrd * sinLat * sinLat);

        // Compute elevation
        llaPosition.z = (p / cos(latRad)) - N;

        return llaPosition;
    }
    catch (const Exception& e)
    {
        throw Exception("GeospatialFunctions", __FUNCTION__, __LINE__, e);
    }
    catch (const std::exception& e)
    {
        throw Exception("GeospatialFunctions", __FUNCTION__, __LINE__, e.what());
    }
    catch (...)
    {
        throw Exception("GeospatialFunctions", __FUNCTION__, __LINE__);
    }
}

Run the function in parallel via std::transform. Note the '&' in the brackets. This indicates the lambda function parameter can use variable external to its body.

void GeospatialFunctions::ConvertXYZToLLA(const glm::mat4& transformMatrix,
    const std::vector<glm::vec3>& xyzPositions, std::vector<glm::dvec3>& llaPositions) {
    try {
       // This is essentially a parallel foreach statement based on the xzyPositions vector   
        std::transform(std::execution::par, xyzPositions.begin(), xyzPositions.end(),
            llaPositions.begin(), [&](const glm::vec3& xyz) {
                return _convertXYZToLLAd(transformMatrix, xyz);
            });
    }
    catch (const Exception& e)
    {
        throw Exception("Glb2RasterFunctions", __FUNCTION__, __LINE__, e);
    }
    catch (const std::exception& e)
    {
        throw Exception("Glb2RasterFunctions", __FUNCTION__, __LINE__, e.what());
    }
    catch (...)
    {
        throw Exception("Glb2RasterFunctions", __FUNCTION__, __LINE__);
    }
}

GPU

⚠️ **GitHub.com Fallback** ⚠️