并行化此Java代码的最佳方法

时间:2017-11-23 22:31:52

标签: java multithreading parallel-processing

如何在Java中使用Threads来并行化这段代码?它从图像中提取所有轮廓,并创建仅具有图像轮廓的新图像。

import java.io.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.awt.Color;

public class Contornos {

static int h, w;

static float debugTime;

public static void main(String[] args) {
    try {

        File fichImagen = new File("test.jpg");

        BufferedImage image = ImageIO.read(fichImagen);

        w = image.getWidth();
        h = image.getHeight();

        int[] inicial = new int[w * h];

        int[] resultadoR = new int[w * h];
        int[] resultadoG = new int[w * h];
        int[] resultadoB = new int[w * h];

        int[][] procesarR = new int[h][w];
        int[][] procesarG = new int[h][w];
        int[][] procesarB = new int[h][w];

        int[][] procesarBN = new int[h][w];

        int[][] binaria = new int[h][w];

        int[] resultado = new int[w * h];

        image.getRGB(0, 0, w, h, inicial, 0, w);

        for (int i = 0; i < w * h; i++) {
            Color c = new Color(inicial[i]);
            resultadoR[i] = c.getRed();
            resultadoG[i] = c.getGreen();
            resultadoB[i] = c.getBlue();
        }

        int k = 0;
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                procesarR[i][j] = resultadoR[k];
                procesarG[i][j] = resultadoG[k];
                procesarB[i][j] = resultadoB[k];
                k++;
            }
        }

        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {

                procesarBN[i][j] = (int) (0.2989 * procesarR[i][j] + 0.5870 * procesarG[i][j] + 0.1140 * procesarB[i][j]);

            }
        }


        binaria = extraerContornos(procesarBN);

        k = 0;
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                resultado[k++] = binaria[i][j];
            }
        }

        image.setRGB(0, 0, w, h, resultado, 0, w);
        ImageIO.write(image, "JPG", new File("allJPG.jpg"));

    } catch (IOException e) {
    }

}

static void debugStart() {
    debugTime = System.nanoTime();
}

static void debugEnd() {
    float elapsedTime = System.nanoTime()-debugTime;

    System.out.println( (elapsedTime/1000000) + " ms ");  
}

private static int[][] extraerContornos(int[][] matriz) {
    int modx, mody;

    int[][] sobelx = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
    int[][] sobely = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};

    int[][] modg = new int[h][w];
    double[][] theta = new double[h][w];
    int[][] thetanor = new int[h][w];
    int[][] contorno = new int[h][w];

    int umbral = 10;
    int superan = 0, ncontorno = 0;
    double t;
    int signo;
    int uno, dos;

    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            if (i == 0 || i == h - 1 || j == 0 || j == w - 1) {
                modg[i][j] = 0;
                theta[i][j] = 0.0;
                thetanor[i][j] = 0;
            } else {
                modx = 0;
                mody = 0;
                for (int k = -1; k <= 1; k++) {
                    for (int l = -1; l <= 1; l++) {
                        modx += matriz[i + k][j + l] * sobelx[k + 1][l + 1];
                        mody += matriz[i + k][j + l] * sobely[k + 1][l + 1];
                    }
                }
                modx = modx / 4;
                mody = mody / 4;

                modg[i][j] = (int) Math.sqrt(modx * modx + mody * mody);

                theta[i][j] = Math.atan2(mody, modx);
                thetanor[i][j] = (int) (theta[i][j] * 256.0 / (2.0 * Math.PI));
            }
        }
    }

    for (int i = 1; i < h - 1; i++) {
        for (int j = 1; j < w - 1; j++) {
            contorno[i][j] = 0;
            if (modg[i][j] >= umbral) {
                superan++;
                t = Math.tan(theta[i][j]);
                if (t >= 0.0) {
                    signo = 1;
                } else {
                    signo = -1;
                }
                if (Math.abs(t) < 1.0) {
                    uno = interpolar(modg[i][j + 1], modg[i - signo][j + 1], t);
                    dos = interpolar(modg[i][j - 1], modg[i + signo][j - 1], t);
                } else {
                    t = 1 / t;
                    uno = interpolar(modg[i - 1][j], modg[i - 1][j + signo], t);
                    dos = interpolar(modg[i + 1][j], modg[i + 1][j - signo], t);
                }
                if (modg[i][j] > uno && modg[i][j] >= dos) {
                    ncontorno++;
                    contorno[i][j] = 255;
                }
            }
        }
    }

    debugEnd();

    return contorno;

}

private static int interpolar(int valor1, int valor2, double tangente) {
    return (int) (valor1 + (valor2 - valor1) * Math.abs(tangente));
}
}

我相信我可以在extraerContornos方法(for for循环)中使用Threads,在最后使用join()来获取结果,但这只是我的猜测。

这是一种正确的并行化方法吗?关于如何知道何时何地开始并行化任何代码的一般提示?

1 个答案:

答案 0 :(得分:2)

一般提示如何知道何时何地开始并行化任何代码?

嗯,从未开始并行化任何代码,如果没有定量支持的证据,它将提高系统性能。

永远不会,
即使有任何院士或崇拜大师告诉你这样做。

首先收集相当数量的证据,它具有任何感觉,并且这样的代码重新设计带来的积极优势将超过原始的纯粹 [SERIAL]代码执行流程。

就像在自然界中或在商业中一样 - 谁会为获得相同的结果多付一分钱?

谁将以目前的工资率支付X- [man * hours]工作,以获得性能上的第一个1.01x改进(不是说想要提供甚至比原始性能更差的想要平行的帮派...因为在加载开销的隐藏成本之前没见过 - 谁会为此付钱?

如何开始分析v / s负面影响可能带来的好处?

首先,尝试理解&#34;机制&#34;,如何分层复合系统 - 由[O / S内核,编程语言,用户程序组成] ] - 使用&#34;只需&#34; - [CONCURRENT] 或true- [PARALLEL] 进程安排来协调前进。 / p>

在不知道这一点的情况下,人们永远无法量化进入的实际成本,有时人们甚至在没有意识到的情况下支付所有这些成本,结果处理流程从来没有至少是#34;只是&#34; ; - [CONCURRENT]处理(如果忘记理解中心&#34;并发防止独占锁定&#34;阻止python GIL锁定,这可能有助于掩盖某些类型的I / O延迟,但从来没有任何改进的CPU绑定处理性能,但所有必须支付产生流程执行的完整副本 - 环境+ python-internal-state的所有巨大成本 - 所有这一切都是为了在最后得到任何东西。没有。是的,如果事情变得糟糕或缺失,那么可能事情就会变得糟糕,以及“并行化”和“行动主义”。

好的,一旦你对操作系统感觉很舒服,那么#mechan;&34;可用于产生线程和流程,您可以猜测或更好地衡量这样做的成本 - 开始定量工作 - 知道需要支付多少[ns] 来产生第一个,第二个,... thirtyninth子线程或单独的操作系统进程,或者使用某些更高级别的语言构造函数的附加成本,即扇出一大堆线程/进程,分发一些工作量,最后将大量结果收集回原始请求者(仅使用.map(...){...}.foreach(...){...}等人的高级语法,这些语法在其下端完成了所有看不见的脏工作。用户程序设计师(不是说&#34;只是&#34; - 编码人员,他们甚至不会花费任何努力来完全负责地理解&#34;机制&#34; +&#34;经济&#34;他们&#34;正常&#34;编码的工作))。

不知道[ns]中的实际费用(技术上没有为了清晰和简洁in Fig.1, that are principally always present, being detailed and discussed in the trailer sections而描述),任何人尝试阅读和尝试几乎都没有意义全面了解其代码设计背景the criticism of the Amdahl's Law

支付最终会收到的人数太多了......

有关此风险的详细信息,请查看this and follow the link from the first paragraph, leading to a fully interactive GUI-simulator of the actual costs of overheads, once introduced into the costs/benefits formula.

返回您的代码:

Sobel-filter内核引入(naive - ) - 线程映射非本地依赖关系,最好从一个简单的部分开始,其中绝对独立性是直接可见的:

可以保存所有重复的for(){...} - 构造函数开销并提高性能:

    for (     int i = 0; i < h; i++ ) {
        for ( int j = 0; j < w; j++ ) {

            Color c = new Color( inicial[i * w + j] );

            procesarBN[i][j] = (int) ( 0.2989 * c.getRed()
                                     + 0.5870 * c.getGreen()
                                     + 0.1140 * c.getBlue()
                                       );
        }
    }

而不是这些triple-for(){...} - s:

    for (int i = 0; i < w * h; i++) {
        Color c = new Color(inicial[i]);
        resultadoR[i] = c.getRed();
        resultadoG[i] = c.getGreen();
        resultadoB[i] = c.getBlue();
    }

    int k = 0;
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            procesarR[i][j] = resultadoR[k];
            procesarG[i][j] = resultadoG[k];
            procesarB[i][j] = resultadoB[k];
            k++;
        }
    }

    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {

            procesarBN[i][j] = (int) (0.2989 * procesarR[i][j] + 0.5870 * procesarG[i][j] + 0.1140 * procesarB[i][j]);

        }
    }

效果?

在Amdahl法律的 [SERIAL] 部分:

  • 净零附加费用:改进/消除了for(){...} - 构造函数循环开销成本的2/3
  • 净零附加费用:改善/取消( 4 * h * w * 3 ) - memIO (即不支付~h * w * 1.320+ [我们]每个!!!)
  • 净零附加费用:改进/淘汰了( 4 * h * w * 3 * 4 ) - memALLOCs ,再次在[TIME]中节省了大量资源和[SPACE],复杂性ZOO分类法的多项式缩放域。

并且在 [CONCURRENT] 处理中运行这些也感觉很安全,因为这个像素值处理在这里主要是独立的(但不在Sobel中,而不在轮廓检测器中)算法)。

所以,在这里,
任何 [CONCURRENT] [PARALLEL] 流程安排可能有所帮助,如果

  • 一些非零附加成本时,处理可以利用多个计算资源(超过 1 CPU-core ,这是在原始版本中运行的, pure - [SERIAL],代码执行,将安全地像素网格映射到这样的(可用资源支持的)线程池或其他代码处理工具上。

然而,
任何非 [SERIAL] 的尝试都是有道理的,当且仅当所有流程分配/解除分配等附加成本的块状物至少得到通过增加 [CONCURRENT] 处理的计算量来证明这一点。

支付超过收款绝对不是明智之举...

因此,基准,基准和基准,然后才能决定什么可能对生产代码产生积极影响。

始终尝试在纯 [SERIAL] 部分进行改进,因为这些部分具有零附加成本,但可能会缩短整体处理时间。

Q.E.D。上方。