论坛首页 Java企业应用论坛

淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和

浏览 94802 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-07-13  
captmjc 写道
来不及看所有的恢复,但是提醒大家注意,在注意算法的同时,注意越界的问题。

两个int相加,都可能越界,何况“很大的List”。所以实战的话,可能需要BigInteger。

而BigInteger的话,更体现了线程的优势。N个较小的BI相加,比一个巨大的BI参与的计算快多了。

同意你说的越界问题,不过采用BigInteger我倒没试过,因为它的加减乘除等操作是程序实现的,不一定比(+-*/)操作符快吧。
0 请登录后投票
   发表时间:2010-07-13  
dilantaya 写道
viei 写道
个人觉的你的这些算法啊,线程操作可能都是白忙活,或者说对这种问题处理的很浅,还不深入。算法挺简单的实现起来也有很多办法,多线程调度什么的也都是基本java知识。
我个人觉的的你要抓住问题的重点,要是用多cpu充分发挥多cpu的优势,首先要把任务分发,你只是起多个线程并不一定操作jvm和操作系统就会把任务分发到多cpu上执行,只有可能时间片切的更小,在执行这些任务。
所以这个过程中考虑的重点应该是
1:操作系统(开多个jvm,规定每个jvm进程运行在指定cpu上,肯定比一个进程下的多个线程只占用一个cpu对多cpu的压榨更好)
2:jvm调整(大数据量必然涉及到垃圾回收)
3:程序编写
上面这些弄好了,再深入一点就考虑,同步消耗问题
cpu多了,同步因素也是决定是否能把多cpu和性能转化率提高出来的一个重要环节。




怎么指定一个jvm对应单个cpu ,高手?


多谢你提出很好的意见。这个就更深入了,我这方便还不太知道,我测试机上只有一个CPU,没法指定,所以只能这么做。你说的方式能不能大概的用代码表示一下,伪代码也行。
0 请登录后投票
   发表时间:2010-07-13  
mapreduce 是什么东东啊!
0 请登录后投票
   发表时间:2010-07-13  
这还是 多线程的问题 啊 还是没有解决多CPU处理的问,你的多线程怎么能保证是多个CPU在处理呢 ,可能还是一个CPU在处理啊 。以前看到过报道好像说是 java7 支持多CPU处理任务。 等待中。。。
0 请登录后投票
   发表时间:2010-07-13  
pengpeng99bill 写道
这还是 多线程的问题 啊 还是没有解决多CPU处理的问,你的多线程怎么能保证是多个CPU在处理呢 ,可能还是一个CPU在处理啊 。以前看到过报道好像说是 java7 支持多CPU处理任务。 等待中。。。



可能,你误会了。Java 1.2之后,使用的操作系统内核线程,操作系统还是会利用多CPU的,也就是说和Java无关了。
我的《Java内存模型》正在写,其中起到了这些东西,如果有需要的话,可以关注一下。
0 请登录后投票
   发表时间:2010-07-13  
如果我做的话,这就是一个分治合并的思想在里面。

分多少完全看你线程数目

一般来说我们完全可以开2N + 1个线程来做运算

你可以使用CountDownLatch,信号量,或者你提到的栅栏都可以。

有个漏斗理论不知道你看过没有,合并的任务就好像漏斗一样。。
0 请登录后投票
   发表时间:2010-07-13  
dilantaya 写道
viei 写道
个人觉的你的这些算法啊,线程操作可能都是白忙活,或者说对这种问题处理的很浅,还不深入。算法挺简单的实现起来也有很多办法,多线程调度什么的也都是基本java知识。
我个人觉的的你要抓住问题的重点,要是用多cpu充分发挥多cpu的优势,首先要把任务分发,你只是起多个线程并不一定操作jvm和操作系统就会把任务分发到多cpu上执行,只有可能时间片切的更小,在执行这些任务。
所以这个过程中考虑的重点应该是
1:操作系统(开多个jvm,规定每个jvm进程运行在指定cpu上,肯定比一个进程下的多个线程只占用一个cpu对多cpu的压榨更好)
2:jvm调整(大数据量必然涉及到垃圾回收)
3:程序编写
上面这些弄好了,再深入一点就考虑,同步消耗问题
cpu多了,同步因素也是决定是否能把多cpu和性能转化率提高出来的一个重要环节。




怎么指定一个jvm对应单个cpu ,高手?



在linux下你看一下cpuinfo获取cpu信息,编号
然后启动的时候和运行的时候都可以通过taskset命令进行cpu绑定
如果开多进程处理,那么进程间数据如何共享,这个还要重新考虑
你现在的程序都是单进程下的
0 请登录后投票
   发表时间:2010-07-13   最后修改:2010-07-13
package com.wl.test.concurrent.semaphore;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 
 * 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。
 * 
 * 
 * 乍一看到题目“充分利用多核CPU”,
 * 以为会根据CPU的核心数,计算出比较合理的任务线程的数目。
 * 就题目的计算目标来说,实际上讲的主线程等待任务线程完成,
 * 任务线程之间没有必要互相等待。
 * 是不是可以考虑用信号来......
 * 每次来看帖,都发现大家图画的很拉风……
 * 
 * 我也帖个代码,虽然来晚了,大家不一定能看到……
 * 
 * @author HardPass
 *
 */
public class BigListSumWithSemaphore {
	private List<Integer> list = new ArrayList<Integer>();

	private AtomicLong sum = new AtomicLong(0L); // 结果
	private int threadCounts = 2 * Runtime.getRuntime().availableProcessors() + 1; // 任务线程数
	private List<Runnable> tasks = new ArrayList<Runnable>(); // 任务线程
	
	SemaphoreLock lock = new SemaphoreLock();
	


	public static void main(String[] args) {
		new BigListSumWithSemaphore().test();
	}

	public void test() {
		init();
		ExecutorService exec = Executors.newFixedThreadPool(threadCounts); //线程池
		for(Runnable task : tasks){
			exec.execute(task);
		}
		lock.lockHere(); // 此处尝试wait
		exec.shutdown();
		System.out.println("List中所有整数的和为:" + sum);
	}

	private void init() {
		for (int i = 1; i <= 1000000; i++) {
			list.add(i);
		}
		int len = list.size() / threadCounts;

		int i = 0;
		for (; i < threadCounts - 1; i++) {
			tasks.add(new Task(list.subList(i * len, (i + 1) * len)));
		}
		tasks.add(new Task(list.subList(i * len, list.size())));
	}

	private class Task implements Runnable {
		private List<Integer> subList;

		public Task(List<Integer> subList) {
			this.subList = subList;
		}

		@Override
		public void run() {
			lock.lockThere();  // 增加锁的计数
			long subSum = 0L;
			for (Integer i : subList) {
				subSum += i;
			}
			sum.addAndGet(subSum);
			System.out.println("分配给线程:" + Thread.currentThread().getName()
					+ "那一部分List的整数和为:\tSubSum:" + subSum);
			lock.release(); // 释放一个锁的计数
		}
	}

}

/**
 * 
 * 信号量锁
 * 
 * 此Semaphore非java.util.concurrent.Semaphore
 * 
 * @author HardPass
 *
 */
class SemaphoreLock {
	private int count = 0; // 信号量
	/**
	 * 信号量大于0的时候 wait
	 * 这是不是传说中的可重入?
	 */
	public synchronized void lockHere() {
		while (count > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
	}

	public synchronized void lockThere() {
		count++;
	}

	public synchronized void release() {
		count--;
		if(count==0){
			notify();
		}
	}
}
0 请登录后投票
   发表时间:2010-07-13  
hardPass 写道
package com.wl.test.concurrent.semaphore;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
*
* 乍一看到题目“充分利用多核CPU”,
* 以为会根据CPU的核心数,计算出比较合理的任务线程的数目。
* 就题目的计算目标来说,实际上讲的主线程等待任务线程完成,
* 任务线程之间没有必要互相等待。
* 是不是可以考虑用信号来......
* 每次来看帖,都发现大家图画的很拉风……
*
* 我也帖个代码,虽然来晚了,大家不一定能看到……
*
* @author HardPass
*
*/
public class BigListSumWithSemaphore {
private List<Integer> list = new ArrayList<Integer>();

private AtomicLong sum = new AtomicLong(0L); // 结果
private int threadCounts = 2 * Runtime.getRuntime().availableProcessors() + 1; // 任务线程数
private List<Runnable> tasks = new ArrayList<Runnable>(); // 任务线程

SemaphoreLock lock = new SemaphoreLock();



public static void main(String[] args) {
new BigListSumWithSemaphore().test();
}

public void test() {
init();
ExecutorService exec = Executors.newFixedThreadPool(threadCounts); //线程池
for(Runnable task : tasks){
exec.execute(task);
}
lock.lockHere();
exec.shutdown();
System.out.println("List中所有整数的和为:" + sum);
}

private void init() {
for (int i = 1; i <= 1000000; i++) {
list.add(i);
}
int len = list.size() / threadCounts;

int i = 0;
for (; i < threadCounts - 1; i++) {
tasks.add(new Task(list.subList(i * len, (i + 1) * len)));
}
tasks.add(new Task(list.subList(i * len, list.size())));
}

private class Task implements Runnable {
private List<Integer> subList;

public Task(List<Integer> subList) {
this.subList = subList;
}

@Override
public void run() {
lock.lockThere();
long subSum = 0L;
for (Integer i : subList) {
subSum += i;
}
sum.addAndGet(subSum);
System.out.println("分配给线程:" + Thread.currentThread().getName()
+ "那一部分List的整数和为:\tSubSum:" + subSum);
lock.release();
}
}

}

/**
*
* 信号量锁
*
* 此Semaphore非java.util.concurrent.Semaphore
*
* @author HardPass
*
*/
class SemaphoreLock {
private int count = 0; // 信号量
/**
* 信号量大于0的时候 wait
* 这是不是传说中的可重入?
*/
public synchronized void lockHere() {
while (count > 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
}

public synchronized void lockThere() {
count++;
}

public synchronized void release() {
count--;
if(count==0){
notify();
}
}
}


java不是有Semaphore,为啥你非要自己写个lock
0 请登录后投票
   发表时间:2010-07-13  
amigo 写道
dilantaya 写道
sunwenran 写道
赞一个。

问题是我用你的例子比较直接加
        long noCurrentSum=0L;  
        for(Integer i:list){  
            noCurrentSum+=i;  
        } 
发现时间差不多。而且有时候直接加更快。纠结了。。我的是双核。


可能是你给的数据量还不够大


调整到50000000
for (int i = 1; i <= 50000000; i++) {
list.add(i);
}


单线程时间比并发更快

呵呵其实这种纯粹的算术相加速度单线程和多线程是一样的(因为没有io读写,没有网络等待等等,),新建线程还要消耗资源,如何设计利用多核cpu就涉及到jvm的底层实现了,所以要我回答这个问题,我给出下面答案:
int sum=0;
for(int i=0;i<list.size();i++)
{
sum+=list.get(i);
}
return sum;
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics