<commit_before>/**
 * @file
 *
 * @brief Contains the @ref launch
 *
 * @note Launch configurations are  used mostly in @ref kernel_launch.hpp . 
 */

#pragma once
#ifndef CUDA_API_WRAPPERS_LAUNCH_CONFIG_BUILDER_CUH_
#define CUDA_API_WRAPPERS_LAUNCH_CONFIG_BUILDER_CUH_

#include "launch_configuration.hpp"
#include "kernel.hpp"
#include "device.hpp"

namespace cuda {


namespace detail_ {

struct no_value_t {};

// This is pretty unsafe; don't use this at home, kids!
template <typename T>
struct poor_mans_optional {
	static_assert(::std::is_trivially_destructible<T>::value, "Use a simpler type");
	union maybe_value_union_t {
		no_value_t no_value;
		T value;
	};

	poor_mans_optional& operator=(const T& value) {
		is_set = true;
		maybe_value.value = value;
		return *this;
	}

	poor_mans_optional& operator=(const T&& value) {
		is_set = true;
		maybe_value.value = ::std::move(value);
		return *this;
	}

	poor_mans_optional& operator=(no_value_t) {
		is_set = false;
		return *this;
	}

	poor_mans_optional& operator=(T&& value) { return *this = value; }
	poor_mans_optional() noexcept : maybe_value{ no_value_t{} } { }
	poor_mans_optional(T v) : is_set(true) {
		maybe_value.value = v;
	}
	poor_mans_optional(const poor_mans_optional& other)
	{
		if (other) {
			*this = other.value();
		}
		is_set = other.is_set;
	}
	~poor_mans_optional() noexcept { };

	T value() const { return maybe_value.value; }

	operator bool() const noexcept { return is_set; }
	void clear() noexcept { is_set = false; }
	void unset() noexcept { is_set = false; }

	bool is_set { false };
	maybe_value_union_t maybe_value;
};


} // namespace detail_

namespace grid {

namespace detail_ {

inline dimension_t div_rounding_up(overall_dimension_t dividend, block_dimension_t divisor)
{
	dimension_t quotient = (dimension_t) dividend / divisor;
		// It is up to the caller to ensure we don't overlow the dimension_t type
	return (divisor * quotient == dividend) ? quotient : quotient + 1;
}

inline dimensions_t div_rounding_up(overall_dimensions_t overall_dims, block_dimensions_t block_dims)
{
	return {
		div_rounding_up(overall_dims.x, block_dims.x),
		div_rounding_up(overall_dims.y, block_dims.y),
		div_rounding_up(overall_dims.z, block_dims.z)
	};
}

// Note: We're not implementing a grid-to-block rounding up here, since - currently -
// block_dimensions_t is the same as grid_dimensions_t.

} // namespace detail_

} // namespace grid

class launch_config_builder_t {
public:
	void resolve_dimensions()  {
		grid::composite_dimensions_t cd = get_composite_dimensions();
		dimensions_.block = cd.block;
		dimensions_.grid = cd.grid;
		if (not dimensions_.overall) {
			dimensions_.overall = cd.grid * cd.block;
		}
	}

protected:
	memory::shared::size_t  get_dynamic_shared_memory_size(grid::block_dimensions_t block_dims) const
	{
		return (memory::shared::size_t)	((dynamic_shared_memory_size_determiner_ == nullptr) ?
			dynamic_shared_memory_size_ :
			dynamic_shared_memory_size_determiner_((int) block_dims.volume()));
			// Q: Why the need for type conversion?
			// A: MSVC is being a bit finicky here for some reason
	}

	grid::composite_dimensions_t get_composite_dimensions() const noexcept(false)
	{
		grid::composite_dimensions_t result;

		if (saturate_with_active_blocks_ and use_min_params_for_max_occupancy_) {
				throw ::std::logic_error(
					"We cannot both use the minimum grid parameters for achieving maximum occupancy, _and_ saturate "
					"the grid with fixed-size cubs.");
		}
		if (saturate_with_active_blocks_) {
			if (not (kernel_)) {
				throw ::std::logic_error("A kernel must be set to determine how many blocks are required to saturate the device");
			}
			if (not (dimensions_.block)) {
				throw ::std::logic_error("The block dimensions must be known to determine how many of them one needs for saturating a device");
			}
			if (dimensions_.grid or dimensions_.overall) {
				throw ::std::logic_error("Conflicting specifications: Grid or overall dimensions specified, but requested to saturate kernels with active blocks");
			}

			result.block = dimensions_.block.value();
			auto dshmem_size = get_dynamic_shared_memory_size(dimensions_.block.value());
			auto num_block_threads = (grid::block_dimension_t) dimensions_.block.value().volume();
			result.grid = kernel_->max_active_blocks_per_multiprocessor(num_block_threads, dshmem_size);
		}
		else if (use_min_params_for_max_occupancy_) {
			if (not (kernel_)) {
				throw ::std::logic_error("A kernel must be set to determine the minimum grid parameter sfor m");
			}
			if (dimensions_.block or dimensions_.grid or dimensions_.overall) {
				throw ::std::logic_error("Conflicting specifications: Grid or overall dimensions specified, but requested to saturate kernels with active blocks");
			}
			auto composite_dims = dynamic_shared_memory_size_determiner_ ?
				kernel_->min_grid_params_for_max_occupancy(dynamic_shared_memory_size_determiner_) :
				kernel_->min_grid_params_for_max_occupancy(dynamic_shared_memory_size_);
			result.block = composite_dims.block;
			result.grid = composite_dims.grid;
		}
		else {
			if ((not dimensions_.block and not dimensions_.grid) or not dimensions_.overall) {
				throw ::std::logic_error(
					"Neither block nor grid dimensions have been specified - cannot resolve launch grid dimenions");
			} else if (dimensions_.block and dimensions_.overall) {
				result.grid = grid::detail_::div_rounding_up(dimensions_.overall.value(), dimensions_.block.value());
				result.block = dimensions_.block.value();
			} else if (dimensions_.grid) {
				result.block = grid::detail_::div_rounding_up(dimensions_.overall.value(), dimensions_.grid.value());
				result.grid = dimensions_.grid.value();
			} else {
				result.block = dimensions_.block.value();
				result.grid = dimensions_.grid.value();
			}
		}
#ifndef NDEBUG
		validate_composite_dimensions(result);
#endif
		return result;
	}
<commit_msg>Fixes #369: Correct launch config builder logic for specifying grid dims.<commit_after>/**
 * @file
 *
 * @brief Contains the @ref launch
 *
 * @note Launch configurations are  used mostly in @ref kernel_launch.hpp . 
 */

#pragma once
#ifndef CUDA_API_WRAPPERS_LAUNCH_CONFIG_BUILDER_CUH_
#define CUDA_API_WRAPPERS_LAUNCH_CONFIG_BUILDER_CUH_

#include "launch_configuration.hpp"
#include "kernel.hpp"
#include "device.hpp"

namespace cuda {


namespace detail_ {

struct no_value_t {};

// This is pretty unsafe; don't use this at home, kids!
template <typename T>
struct poor_mans_optional {
	static_assert(::std::is_trivially_destructible<T>::value, "Use a simpler type");
	union maybe_value_union_t {
		no_value_t no_value;
		T value;
	};

	poor_mans_optional& operator=(const T& value) {
		is_set = true;
		maybe_value.value = value;
		return *this;
	}

	poor_mans_optional& operator=(const T&& value) {
		is_set = true;
		maybe_value.value = ::std::move(value);
		return *this;
	}

	poor_mans_optional& operator=(no_value_t) {
		is_set = false;
		return *this;
	}

	poor_mans_optional& operator=(T&& value) { return *this = value; }
	poor_mans_optional() noexcept : maybe_value{ no_value_t{} } { }
	poor_mans_optional(T v) : is_set(true) {
		maybe_value.value = v;
	}
	poor_mans_optional(const poor_mans_optional& other)
	{
		if (other) {
			*this = other.value();
		}
		is_set = other.is_set;
	}
	~poor_mans_optional() noexcept { };

	T value() const { return maybe_value.value; }

	operator bool() const noexcept { return is_set; }
	void clear() noexcept { is_set = false; }
	void unset() noexcept { is_set = false; }

	bool is_set { false };
	maybe_value_union_t maybe_value;
};


} // namespace detail_

namespace grid {

namespace detail_ {

inline dimension_t div_rounding_up(overall_dimension_t dividend, block_dimension_t divisor)
{
	dimension_t quotient = (dimension_t) dividend / divisor;
		// It is up to the caller to ensure we don't overlow the dimension_t type
	return (divisor * quotient == dividend) ? quotient : quotient + 1;
}

inline dimensions_t div_rounding_up(overall_dimensions_t overall_dims, block_dimensions_t block_dims)
{
	return {
		div_rounding_up(overall_dims.x, block_dims.x),
		div_rounding_up(overall_dims.y, block_dims.y),
		div_rounding_up(overall_dims.z, block_dims.z)
	};
}

// Note: We're not implementing a grid-to-block rounding up here, since - currently -
// block_dimensions_t is the same as grid_dimensions_t.

} // namespace detail_

} // namespace grid

class launch_config_builder_t {
public:
	void resolve_dimensions()  {
		grid::composite_dimensions_t cd = get_composite_dimensions();
		dimensions_.block = cd.block;
		dimensions_.grid = cd.grid;
		if (not dimensions_.overall) {
			dimensions_.overall = cd.grid * cd.block;
		}
	}

protected:
	memory::shared::size_t  get_dynamic_shared_memory_size(grid::block_dimensions_t block_dims) const
	{
		return (memory::shared::size_t)	((dynamic_shared_memory_size_determiner_ == nullptr) ?
			dynamic_shared_memory_size_ :
			dynamic_shared_memory_size_determiner_((int) block_dims.volume()));
			// Q: Why the need for type conversion?
			// A: MSVC is being a bit finicky here for some reason
	}

	grid::composite_dimensions_t get_composite_dimensions() const noexcept(false)
	{
		grid::composite_dimensions_t result;

		if (saturate_with_active_blocks_ and use_min_params_for_max_occupancy_) {
				throw ::std::logic_error(
					"We cannot both use the minimum grid parameters for achieving maximum occupancy, _and_ saturate "
					"the grid with fixed-size cubs.");
		}
		if (saturate_with_active_blocks_) {
			if (not (kernel_)) {
				throw ::std::logic_error("A kernel must be set to determine how many blocks are required to saturate the device");
			}
			if (not (dimensions_.block)) {
				throw ::std::logic_error("The block dimensions must be known to determine how many of them one needs for saturating a device");
			}
			if (dimensions_.grid or dimensions_.overall) {
				throw ::std::logic_error("Conflicting specifications: Grid or overall dimensions specified, but requested to saturate kernels with active blocks");
			}

			result.block = dimensions_.block.value();
			auto dshmem_size = get_dynamic_shared_memory_size(dimensions_.block.value());
			auto num_block_threads = (grid::block_dimension_t) dimensions_.block.value().volume();
			result.grid = kernel_->max_active_blocks_per_multiprocessor(num_block_threads, dshmem_size);
		}
		else if (use_min_params_for_max_occupancy_) {
			if (not (kernel_)) {
				throw ::std::logic_error("A kernel must be set to determine the minimum grid parameter sfor m");
			}
			if (dimensions_.block or dimensions_.grid or dimensions_.overall) {
				throw ::std::logic_error("Conflicting specifications: Grid or overall dimensions specified, but requested to saturate kernels with active blocks");
			}
			auto composite_dims = dynamic_shared_memory_size_determiner_ ?
				kernel_->min_grid_params_for_max_occupancy(dynamic_shared_memory_size_determiner_) :
				kernel_->min_grid_params_for_max_occupancy(dynamic_shared_memory_size_);
			result.block = composite_dims.block;
			result.grid = composite_dims.grid;
		}
		else {
			if (not dimensions_.block and not dimensions_.grid) {
				throw ::std::logic_error(
					"Neither block nor grid dimensions have been specified");
			} else if (not dimensions_.block and not dimensions_.overall) {
				throw ::std::logic_error(
					"Grid dimensions only been specified in terms of blocks, not threads, and no block dimensions specified");
			} else if (not dimensions_.block and not dimensions_.overall) {
				throw ::std::logic_error(
					"Only block dimensions have been specified - cannot resolve launch grid dimensions");
			}
			// Ok, we're sure we can resolve the dimensions somehow
			if (dimensions_.block and dimensions_.overall) {
				result.grid = grid::detail_::div_rounding_up(dimensions_.overall.value(), dimensions_.block.value());
				result.block = dimensions_.block.value();
			} else if (dimensions_.grid) {
				result.block = grid::detail_::div_rounding_up(dimensions_.overall.value(), dimensions_.grid.value());
				result.grid = dimensions_.grid.value();
			} else {
				result.block = dimensions_.block.value();
				result.grid = dimensions_.grid.value();
			}
		}
#ifndef NDEBUG
		validate_composite_dimensions(result);
#endif
		return result;
	}

{'fraction_non_alphanumeric': 0.10285992850178746, 'fraction_numerical': 0.0004062398440038999, 'mean_word_length': 4.749182624941616, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7044163', 'n_tokens_mistral': 4402, 'n_tokens_neox': 3962, 'n_words': 999}