将 Spark RDD sizes:Casting long 设置为 Double inside 10^9+ for loop,真的很糟糕吗?

Setting Spark RDD sizes:Casting long to Double inside 10^9+ for loop, really bad idea?

(编辑:看看这个问题从哪里开始,它确实在一个更好的地方结束了。当通过 SparkContext.parallelize()与 RDD 的实际大小限制。还发现了一些在用户文档中找不到的 parallelize() 参数。特别查看 zero323 的评论和他接受的答案。)

太阳底下没有什么新鲜事,但我找不到这个问题已经被问过...问题是关于 wrong/inadvisable/improper 它可能如何 运行 在一个大的 for 循环中进行强制转换Java.

我想在将 Arraylist 传递给 SparkContext.parallelize() 方法之前使用 运行 for 循环来初始化 Arraylist。我发现将未初始化的数组传递给 Spark 会导致空集合错误。

我看过很多关于 float 和 double 作为计数器的坏主意的帖子,我明白了,这似乎也是一个坏主意?好像必须有更好的方法?

numListLen 目前为 10^6 * 10^3,在某些时候可能会达到 10^12。

    List<Double> numList = new ArrayList<Double>(numListLen);
    for (long i = 0; i < numListLen; i++) {
        numList.add((double) i);
    }

我很想知道这段代码具体哪里有问题并且可以改进。我是初级CS学生所以我还没有看到所有的角度哈哈。 Here's a CMU page seemingly approving this approach in C 使用隐式转换。


仅作为背景,numList 将被传递给 Spark 以告诉它运行 模拟多少次并用结果创建一个 RDD,如下所示:

JavaRDD数据集=jsc.parallelize(numList,SLICES_AKA_PARTITIONS);

    // the function will be applied to each member of dataSet
    Double count = dataSet.map(new Function<Double, Double>() {...

(实际上,我很想 运行 通过 Spark 创建这个 Arraylist,但它似乎没有足够的时间来保证,在我的 i5 双核上是 5 秒,但如果提升到 10^ 12 然后...更长 )

问题是使用双精度或浮点数作为循环计数器。在您的情况下,循环计数器很长并且不会遇到同样的问题。

将双精度数或浮点数用作循环计数器的一个问题是浮点精度会在表示的数字系列中留下间隙。有可能到达浮点数有效范围内的位置,其中加一低于所表示数字的精度(例如,当浮点格式仅支持 15 位时,需要 16 位)。如果您的循环在正常执行中经过这样一个点,它将不会递增并继续无限循环。

双精度作为循环计数器的另一个问题是比较两个浮点数的能力。四舍五入意味着要成功比较变量,您需要查看范围内的值。虽然您可能会发现 1.0000000 == 0.999999999,但您的计算机不会。所以四舍五入也可能会让你错过循环终止条件。

您的 long as 循环计数器不会出现这些问题。所以享受做对的事情吧。

尽管我不建议使用浮点值(单精度或双精度)作为 for 循环计数器,但在您的情况下,步长不是十进制数(您使用 1 作为一个步骤),一切都取决于你最大的预期数字与双精度表示的小数部分(52 位)。

仍然,2^52..2^53 中的双数正确表示整数部分,但在 2^53 之后,你不能总是达到整数部分的精度。

实际上,因为你的循环步骤是 1,如果你使用 double,直到 9,007,199,254,740,992 之前你不会遇到任何问题] 作为计数器,从而避免投射(你无法避免从 doubleDouble 的拳击)。

执行简单的增量测试;你会看到9,007,199,254,740,995是第一个误报!

仅供参考:对于 float 数字,您可以安全地递增到 2^24 = 16777216(在您提供的 article 中,它使用数字100000001.0f > 16777216 提出问题)。

and already covered problems related to using Doubles as counters and 指出了在循环中创建对象的问题,但归根结底,您根本无法分配大于 Integer.MAX_VALUEArrayList。最重要的是,即使有 231 个元素,这也是一个相当大的对象,序列化和网络流量会给您的工作增加大量开销。

有几种方法可以解决这个问题:

  • 使用SparkContext.range方法:

    range(start: Long, end: Long, 
      step: Long = 1, numSlices: Int = defaultParallelism)
    
  • 正在使用范围对象初始化 RDD。在 PySpark 中,您可以使用或 range (xrange in Python 2), in Scala Range:

    val rdd = sc.parallelize(1L to Long.MaxValue) 
    

    它需要驱动程序上的恒定内存和每个执行程序的恒定网络流量(您只需要传输它的开始和结束)。

    In Java 8 LongStream.range 可以以相同的方式工作,但看起来 JavaSparkContext 还没有提供所需的构造函数。如果你有足够的勇气处理所有的单例和 implicits 你可以直接使用 Scala Range 如果没有你可以简单地写一个 Java 友好的包装器。

  • 使用 emptyRDD 方法/少量种子初始化 RDD 并使用 mapPartitions(WithIndex) / flatMap 填充它。例如参见 [​​=32=]

    只要发挥一点创意,您实际上可以通过这种方式生成无限数量的元素 ()。

  • 鉴于您的特定用例,您还应该看看 mllib.random.RandomRDDs。它提供了许多来自不同发行版的有用生成器。