`

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

    博客分类:
  • JSE
阅读更多

I've seen these two:

http://www.iteye.com/topic/711162?page=13

http://www.iteye.com/topic/713259

 

But I am not sure whether I missed something or the majority missed something. Though the chancee of later is slim, I am going to take the chance.

 

Say we have 100 numbers and we are break them into batches of size 3, 

{1, 2, 3}, {4, 5, 6} .... {97, 98, 99}, {100}

now compute the sums,

6, 15, ..., 294, 100

There are 34 numbers. Now we should break this array again into batches of size 3, right? Why does nobody do this? I am scratching my hair!

 

So here is my code. First, let's create a simple version

 

 

package my.test.brainteaser.sum;

public class SingleThreadCalculator
{
    public long calc(long[] values, int start, int end)
    {
        if (values == null || values.length == 0) return 0;

        long sum = 0;
        for (int i=start; i<end; i++) sum += values[i];
        return sum;
    }
}

 

 

Here we are using long to avoid overflow (java won't raise a flag like fortran for overflow). The extra variables start and end for avoiding array copying later on. These are counterintuitive, we shall talk about them later on.

 

Now let's create a Runnable wrapper for this:

 

 

package my.test.brainteaser.sum;

import java.util.concurrent.CountDownLatch;

public class RunnableCalculator implements Runnable
{
    private SingleThreadCalculator calc;
    private long[] values;
    private int start;
    private int end;
    private long result;

    private CountDownLatch doneSignal;

    public RunnableCalculator(SingleThreadCalculator calc, long[] values, int start, int end, CountDownLatch doneSignal)
    {
        this.calc = calc;
        this.values = values; // for fast performance, no copy
        this.start = start;
        this.end = end;
        this.doneSignal = doneSignal;
    }

    public void run()
    {
        System.out.println("Thread: " + Thread.currentThread() + " start=" + start + " end=" + end);
        result = calc.calc(values, start, end);
        this.doneSignal.countDown();
    }

    public long getResult() { return result; }
}

 

 

The countdown latcher is to wait for all jobs finishes.

 

Now the core code:

 

 

package my.test.brainteaser.sum;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadedCalculator
{
    private int numThreads = 0; // 0 means no threading
    private int partSize = 100;
    private SingleThreadCalculator singleThreadCalculator = new SingleThreadCalculator();

    public long calc(long[] values)
    {
        if (numThreads == 0)
            return singleThreadCalculator.calc(values, 0, values.length);

        if (values == null || values.length == 0) return 0;

        // compute how many parts we can divide
        int len = values.length / partSize;
        if (values.length % partSize != 0) len++;
        long[] sums = new long[len]; // partial results

        CountDownLatch doneSignal = new CountDownLatch(len);

        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
        List<RunnableCalculator> calcs = new ArrayList<RunnableCalculator>(len);
        for (int i=0; i<len; i++)
        {
            int end;
            if (i < len - 1) // not the last one
                end = (i+1) * partSize;
            else // last one
            {
                end = values.length;
            }

            RunnableCalculator rc = new RunnableCalculator(singleThreadCalculator,
                            values, i*partSize, end, doneSignal);
            calcs.add(rc);
            executor.execute(rc);
        }

        try
        {
            doneSignal.await();
            for (int i=0; i<len; i++)
                sums[i] = calcs.get(i).getResult();

            if (sums.length <= partSize)
                return singleThreadCalculator.calc(sums, 0, sums.length);
            else return calc(sums);
        }
        catch (InterruptedException ie)
        {
            throw new RuntimeException("got interrupted", ie);
        }
    }

    public void setNumThreads(int numThreads)
    {
        if (numThreads < 0) this.numThreads = 0;
        this.numThreads = numThreads;
    }

    public void setPartSize(int partSize)
    {
        this.partSize = partSize;
    }
}

 

 

Here we have two variables, one for number of threads, another is for the part size in each thread. They are different, and very important when we want to optimize the performance in the real world.

 

There is a try block, inside, there is a recursive call, this is where we apply the same logic to the intermediate results.

 

The test case is:

 

package my.test.brainteaser.sum;

import junit.framework.TestCase;

public class SummationTest extends TestCase
{
    public void testMultithreading()
    {
        long[] a = new long[10000];
        for (int i=0; i<a.length; i++)
            a[i] = i+1;

        MultiThreadedCalculator c = new MultiThreadedCalculator();
        c.setNumThreads(4);
        c.setPartSize(100);
        long sum = c.calc(a);
        System.out.println("sum=" + sum);
        assertTrue(sum == 10001 * 5000);
    }

    public void testMore()
    {
        MultiThreadedCalculator c = new MultiThreadedCalculator();

        int len = 100000;
        c.setNumThreads(7);
        c.setPartSize(321);
        long sum = c.calc(generateCase(len, true));
        System.out.println("sum=" + sum);
        long res = 1L * len * (len + 1) / 2;
        System.out.println("res=" + res);
        assertTrue(sum == res);
    }

    public void testMore1()
    {
        MultiThreadedCalculator c = new MultiThreadedCalculator();

        int len = 54321;
        c.setNumThreads(7);
        c.setPartSize(123);
        long sum = c.calc(generateCase(len, false));
        System.out.println("sum=" + sum);
        long res = 1L * len * (len + 1) / 2;
        System.out.println("res=" + res);
        assertTrue(sum == res);
    }

    private long[] generateCase(int len, boolean inc)
    {
        long[] ret = new long[len];
        if (inc)
        {
            for (int i=0; i<ret.length; i++)
                ret[i] = i+1;
        }
        else
        {
            for (int i=0; i<ret.length; i++)
                ret[i] = len - i;
        }

        return ret;
    }
}
 

To go further beyond this puzzle, there are two practical concerns:

 

1. We should extract a general interface from SingleThreadCalculator and MultiThreadedCalculator to shield out the runtime environment(single threaded or multi threaded env) so that users can choose. There are quite a few runtime environments I've experienced, such as message based, parallel computing based, in addition to multi-threaded.

 

2. The calculator interface can be abstracted to a general computing job interface which will be passed into a computing runtime environment so that we could run different computings, such as sum, average, or others.

分享到:
评论
15 楼 wensonzhou86 2010-11-18  
思路明确,代码清晰,通俗易懂
14 楼 carydeepbreathing 2010-11-17  
代码比较干净
13 楼 flyingpig4 2010-08-12  
这个面试题是出自淘宝实践的

12 楼 merphine 2010-07-19  
oh my lady gaga
11 楼 nickevin 2010-07-19  
代码和阐述都很漂亮 多了一种思路 自己测试了一下 效率没有使用CylicBarrier高 待研究中...
10 楼 hardPass 2010-07-19  
if (!描述语言.equals(Lang.BIRD))
   print("better");
9 楼 yangyi 2010-07-18  
非常不错,如果之前的帖子体现的是基础知识,那么这一个体现的是lz的智慧
8 楼 david.org 2010-07-18  
> There are 34 numbers. Now we should break this array again into batches of size 3, right? Why does nobody do this? I am scratching my hair!

很不错的主意,赞呀.

1. 中国人的英语, 楼上别怀疑了
2. 代码看上去很舒服, 解释的也很清楚
3. 利用partSize 参数调优, 再赞.
7 楼 seagod.wong 2010-07-18  
turf...........................
6 楼 lz12366 2010-07-18  
oh  my  god  hahaha...
5 楼 zhufeng1981 2010-07-18  
good, support you.
4 楼 xgj1988 2010-07-17  
Oh my  god  ,Bird language
3 楼 fehly 2010-07-17  
szcjlssx 写道
aa87963014 写道
这难道是一位美国人发的贴么


同问,我想是楼主在练英语吧

继续问...估计是外企的.....nr
2 楼 szcjlssx 2010-07-17  
aa87963014 写道
这难道是一位美国人发的贴么


同问,我想是楼主在练英语吧
1 楼 aa87963014 2010-07-17  
这难道是一位美国人发的贴么

相关推荐

    大数据技术sql面试题

    本文档提供了三道大数据技术SQL面试题的解析,涵盖了数据的累积访问次数、店铺UV和PV统计、访客访问次数排名等方面的知识点。 知识点 1:累积访问次数 在第一题中,我们需要使用 SQL 统计出每个用户的累积访问次数...

    经典HiveSQL面试题.docx

    "经典HiveSQL面试题" 在这篇文章中,我们将讨论两个经典的HiveSQL面试题,并详细解释每个问题的解决方案。 问题1:统计每个用户的累积访问次数 在这个问题中,我们需要使用HiveSQL来统计每个用户的累积访问次数。...

    VisIt可视化软件用户手册

    通过以上详细介绍可以看出,VisIt是一款功能齐全且易用的科学数据可视化工具,不仅支持常见的数据格式,还提供了丰富的图表类型和强大的数据库管理功能,极大地提高了科研工作者的数据分析效率。

    linux下从源代码安装32位的visit

    在Linux系统中,从源代码编译安装32位软件,特别是像Visit这样的专业可视化工具,需要遵循一系列步骤。这通常涉及到下载源代码、配置编译环境、编译源代码以及安装程序。以下是一个详尽的步骤指南: 1. **环境准备*...

    asp.net 面试题集合

    ### ASP.NET面试题知识点解析 #### 1. ASP.NET中的几种获取数据的方式及其特点 在ASP.NET开发中,获取数据通常涉及多种方式,包括`QueryString`、`Application`、`Session`、`Cookie`以及`Server.Transfer`等。 -...

    算法面试题

    根据提供的文件内容,我们可以归纳出以下关键算法知识点: ### 1....这些算法是计算机科学的基础,也是很多编程面试中常见的问题类型。理解并掌握这些算法能够帮助求职者在技术面试中取得更好的成绩。

    2008软件公司常问的技术面试题

    ### 2008软件公司常问的技术面试题解析 #### .NET框架及Web服务相关问题 1. **什么是命名空间(Namespace)?** - 命名空间是.NET框架中用来组织类的一种方式,它能够帮助开发人员避免名称冲突的问题。在.NET中,...

    python后端如何记录当前网站各菜单项用户访问次数

    在Python后端开发中,记录网站各菜单项的用户访问次数是一项常见的需求,这有助于分析用户行为、优化网站布局和提升用户体验。以下是如何在Python环境中使用MySQL数据库来实现这一功能的详细步骤: 首先,我们需要...

    visit-me:using使用由Puppeteer和Node-Fetch支持的CLI可以更轻松地访问您的网页

    :beach_with_umbrella: 看望我使用由Puppeteer和Node-Fetch支持的CLI可以更轻松地访问您的网页安装 $ npm i -g visit-me用法 $ visit-me -u [YOUR_WEB_URL]其他选项争论别名描述数据类型默认值--count -c 造访次数...

    peric code

    Peric Code选择用Fortran编写,主要是因为其高效性、内存管理和处理大型数组的能力,这些都是进行大规模数值计算的关键要素。 Peric Code的核心功能可能包括以下方面: 1. **网格生成**:CFD的第一步通常是创建...

    流体力学数值模拟_fluidsimulation_

    9. **并行计算与GPU加速**:由于计算需求巨大,数值模拟常利用并行计算资源,如多核CPU和GPU,以提高计算效率和解决更大规模的问题。 10. **后处理**:模拟完成后,数据需要进行可视化和分析,以理解流体行为。这...

    hadoop-trunk.zip

    For the latest information about Hadoop, please visit our website at: http://hadoop.apache.org/ and our wiki, at: http://wiki.apache.org/hadoop/ This distribution includes cryptographic software. ...

    企业面试sql题(含答案).docx

    总之,此面试题考察了SQL中的基本操作,如数据分组、聚合函数以及窗口函数的使用,这些都是在大数据分析和处理中经常遇到的技能。理解并能熟练运用这些概念对于任何在IT行业,特别是大数据领域工作的专业人士来说至...

    非常好的Visita教程

    Visita是一款强大的三维地表建模和可视化软件,...通过这个教程,无论是对Visita完全陌生的用户还是有经验但需要在Vista环境下工作的专业人士,都能得到实用的指导,提高工作效率,更好地理解和利用Visita的强大功能。

    visual c++ vc 计算一个目录文件及其子目录下所有文件大小总和.zip

    在本文中,我们将深入探讨如何使用Visual C++ (VC++) 和C++编程语言来计算一个目录及其所有子目录下的所有文件大小总和。这个任务可以通过实现深度优先搜索(DFS)或广度优先搜索(BFS)策略来完成,这里我们将重点讨论...

    Best-websites-a-programmer-should-visit-zh-计算机考试习题资源

    Best-websites-a-programmer-should-visit-zh-计算机考试习题资源

    user_visit_action1.txt

    - Spark SQL 可以直接读取 Hive 中定义的表,并利用 Spark 的计算能力执行 SQL 查询,这大大提高了数据分析的效率。 2. **集成配置**: - 首先确保 Spark 和 Hive 版本兼容。 - 在 Spark 的配置文件 `spark-...

    Java笔试题

    - 可以充分利用多核处理器的优势,每个线程处理一个请求,理论上可以实现更高的吞吐量。 - 当访问量很大时,可能会导致系统资源耗尽,因为线程也是资源密集型的。 - 每个线程上下文切换带来额外的开销,可能会影响...

    计算流体力学作业 代码

    计算流体力学(Computational Fluid Dynamics, CFD)是一门涉及多领域交叉的科学,它利用数值分析和计算机科学的方法来研究和模拟流体的行为。在这个"计算流体力学作业"中,我们可以推测这可能是一份来自中山大学的...

    visit.js:为了更容易访问嵌套属性

    注意:visit.js只是一个实验,因为 ,但Firefox和最新的IE除外。 如果您厌倦了这些: // To get `name`, but you do not know whether `employees` and `employees[0]` exist or not const name = ( company . ...

Global site tag (gtag.js) - Google Analytics