面试官:请写出一个函数用于vector<int>的求和,一道题看出你的C++功底

面试官:请写出一个函数用于vector的求和

我:这不是有手就行?代码如下

//Version 1
int accumulate(vector<int>& v) // v注意使用传引用,避免调用拷贝构造函数增加函数运行时间成本
{
	int sum = 0;
	for (int i = 0; i < v.size(); i++)
		sum += v[i];
	return sum;
}

//Version 2
//C++ 11版本for循环
int accumulate(vector<int>& v)
{
	int sum = 0;
	for(int i: v)
		sum += i;
	return sum;
}

面试官:如果vector中数据量很大,调用一次函数要使用的时间很长,要怎么处理呢?

我:咦?有点东西。使用多线程,将vector中的数据拆分成多块,分别计算求和结果,然后在主线程中汇总各个线程的计算结果。

//Version 3
int accumulate_3(vector<int>& v)
{
	if (v.size() == 0)
		return 0;

	//  lambda 表达式,用于计算区间[start, end)中元素的和
	auto accumulate_block = [&v](int start, int end, int& result) { 
		for (int i = start; i < end; i++)
			result += v[i];
		return result;
	};

	// 获得电脑支持的最大线程数
	unsigned int hardware_threads = thread::hardware_concurrency();
	if (hardware_threads == 0) // 程序无法获得线程数,可能电脑不支持多线程
		hardware_threads = 2;

	// 每个线程最少要计算的工作量
	const size_t min_work_per_thread = 25;

	// 获得数组长度
	size_t size = v.size();

	// 可以想一下"size + min_work_per_thread - 1" 如果改成 "size" 会发生什么
	// 例如当size = 49时,"(size + min_work_per_thread - 1) / min_work_per_thread" 的结果为2
	// 而"(size) / min_work_per_thread" 的结果为1(注意int除法是向下取整)
	// 我认为第一种做法更优,当然你如果想用第二种也不是不可以
	unsigned int num_threads = min(hardware_threads, 
		(unsigned int)((size + min_work_per_thread - 1) / min_work_per_thread));

	// 计算每个线程要计算的工作长度
	size_t work_per_thread = size / num_threads;

	vector<thread> threads(num_threads); // 用于存储线程
	vector<int> results(num_threads); // 用于存储线程计算结果

	// 计算每个线程的工作范围
	// 注意for循环范围,只产生num_threads - 1个线程,另外一个线程在for循环外面创建
	for(unsigned int i = 0; i < num_threads - 1; i++)
		threads[i] = thread(accumulate_block, i * work_per_thread,
			(i + 1) * work_per_thread, ref(results[i]));
	threads[num_threads - 1] = thread(accumulate_block, (num_threads - 1) * work_per_thread,
		size, ref(results[num_threads - 1]));

	// 等待所有线程结束
	// 或者使用 for_each(threads.begin(), threads.end(), mem_fn(&thread::join));
	for (thread& t : threads)
		t.join();

	// 计算结果
	int result = 0;
	for (int i : results)
		result += i;
	return result;
}

面试官:怎么将这个求和函数修改,从而支持泛型

我:上面的都能写出来,这个改一改就行了。

//Version 4
//应保证传入的类T重载了'+'运算符
template<typename T>
int accumulate_4(vector<T>& v, T& result) // 注意!这里要传入result用于存储求和结果,避免传入的类没有默认构造函数导致函数编译失败
{
	if (v.size() == 0)
		return 0;

	//  lambda 表达式,用于计算区间和
	auto accumulate_block = [&v](int start, int end, T& result) {
		for (int i = start; i < end; i++)
			result += v[i];
		return result;
	};

	// 获得电脑支持的最大线程数
	unsigned int hardware_threads = thread::hardware_concurrency();
	if (hardware_threads == 0) // 程序无法获得线程数
		hardware_threads = 2;

	// 每个线程最少要计算的工作量
	const size_t min_work_per_thread = 25;

	// 获得数组长度
	size_t size = v.size();

	// 可以想一下"size + min_work_per_thread - 1" 如果改成 "size" 会发生什么
	// 例如当size = 49时,"(size + min_work_per_thread - 1) / min_work_per_thread" 的结果为2
	// 而"(size) / min_work_per_thread" 的结果为1(注意int除法是向下取整)
	// 我认为第一种做法更优,当然你如果想用第二种也不是不可以
	unsigned int num_threads = min(hardware_threads,
		(unsigned int)((size + min_work_per_thread - 1) / min_work_per_thread));

	// 计算每个线程要计算的工作长度
	size_t work_per_thread = size / num_threads;

	vector<thread> threads(num_threads); // 用于存储线程
	vector<T> results(num_threads); // 用于存储线程计算结果

	// 计算每个线程的工作范围
	// 注意for循环范围,只产生num_threads - 1个线程,另外一个线程在for循环外面创建
	for (unsigned int i = 0; i < num_threads - 1; i++)
		threads[i] = thread(accumulate_block, i * work_per_thread,
			(i + 1) * work_per_thread, ref(results[i]));
	threads[num_threads - 1] = thread(accumulate_block, (num_threads - 1) * work_per_thread,
		size, ref(results[num_threads - 1]));

	// 等待所有线程结束
	// 或者使用 for_each(threads.begin(), threads.end(), mem_fn(&thread::join));
	for (thread& t : threads)
		t.join();

	// 计算结果
	for (T& i : results) // 传入引用,避免使用拷贝构造函数
		result += i;
	return result;
}

最后献上《C++并发编程实战》(第二版)书中的实现方式

template<typename Iterator, typename T>
struct accumulate_block
{
	void operator()(Iterator first, Iterator last, T& result)
	{
		result = std::accumulate(first, last, result);
	}
};
template<typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init)
{
	unsigned long const length = std::distance(first, last);
	if (!length) // 1
		return init;
	unsigned long const min_per_thread = 25;
	unsigned long const max_threads =
		(length + min_per_thread - 1) / min_per_thread; // 2
	unsigned long const hardware_threads =
		std::thread::hardware_concurrency();
	unsigned long const num_threads = // 3
		std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);
	unsigned long const block_size = length / num_threads; // 4
	std::vector<T> results(num_threads);
	std::vector<std::thread> threads(num_threads - 1); // 5
	Iterator block_start = first;
	for (unsigned long i = 0; i < (num_threads - 1); ++i)
	{
		Iterator block_end = block_start;
		std::advance(block_end, block_size); // 6
		threads[i] = std::thread( // 7
			accumulate_block<Iterator, T>(),
			block_start, block_end, std::ref(results[i]));
		block_start = block_end; // 8
	}
	accumulate_block<Iterator, T>()(
		block_start, last, results[num_threads - 1]); // 9
	for (auto& entry : threads)
		entry.join(); // 10
	return std::accumulate(results.begin(), results.end(), init); // 11
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值