试图理解 Scala 数组
Trying to understand Scala Array
我正在尝试评估哪种数据结构最能代表 Scala 中的稀疏向量。这些稀疏向量包含一个索引列表,每个索引都有一个值。我实施了一个小型基准测试,这似乎表明 Array[(Long, Double)]
似乎比 2 个并行数组占用的 space 少很多。那是对的吗?我是否正确地执行了该基准测试? (如果我在某处做错了,我不会感到惊讶)
import java.lang.management.ManagementFactory
import java.text.NumberFormat
object TestSize {
val N = 100000000
val formatter: NumberFormat = java.text.NumberFormat.getIntegerInstance
def twoParallelArrays(): Unit = {
val Z1 = Array.ofDim[Long](N)
val Z2 = Array.ofDim[Double](N)
Z1(N-1) = 1
Z2(N-1) = 1.0D
println(Z2(N-1) - Z1(N-1))
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def arrayOfTuples(): Unit = {
val Z = Array.ofDim[(Long, Double)](N)
Z(N-1) = (1, 1.0D)
println(Z(N-1)._2 - Z(N-1)._1)
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def main(args: Array[String]): Unit = {
// Comment one or the other to look at the results
//arrayOfTuples()
twoParallelArrays()
}
}
不,不正确。
Array.ofDim[(Long, Double)](N)
创建一个由 null
填充的大数组,并且不为 Long
、Double
或实际的 Tuple2
实例分配任何 space ,这就是为什么您在 heap-memory 用法中看不到任何内容。
two-array 版本立即为所有 Long
和 Double
分配它需要的所有 space,并且您会在堆 space 用法中看到它。
只需将 ofDim
替换为适当的 fill
即可查看 真实 数字。
在大小为 N = 1000000
的数组上:
arrayOfTuples: 45,693,312 19,190,296
twoParallelArrays: 25,925,792 19,315,256
arrayOfTuples
-解决方案显然需要更多 space。
您可能想知道为什么该系数大约为 1.8 而不是至少 2.5。这是因为Tuple2
对于一些原始数据类型来说是@specialized,特别是对于Long
和Double
,因此这两个8字节的原始数据可以存储在Tuple2
中没有拳击。因此,从数组到 Tuple2
的 64 位引用的总开销仅为 8 个字节,并且每个 Tuple2
实例中都有一些开销。但是,它不仅仅是将 Long
和 Double
直接存储在数组中。
顺便说一句:这正是 Apache Spark 使用所有这些 Encoder
存储数据的原因:这样您就不必担心元组和 case-classes.
完整代码:
import java.lang.management.ManagementFactory
import java.text.NumberFormat
object TestSize {
val N = 1000000
val formatter: NumberFormat = java.text.NumberFormat.getIntegerInstance
def twoParallelArrays(): Unit = {
val Z1 = Array.fill[Long](N)(42L)
val Z2 = Array.fill[Double](N)(42.0)
println(Z1)
println(Z2)
Z1(N-1) = 1
Z2(N-1) = 1.0D
println(Z2(N-1) - Z1(N-1))
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
Z1((new scala.util.Random).nextInt(N)) = 1234L
Z2((new scala.util.Random).nextInt(N)) = 345.0d
println(Z2(N-1) - Z1(N-1))
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def arrayOfTuples(): Unit = {
val Z = Array.fill[(Long, Double)](N)((42L, 42.0d))
Z(N-1) = (1, 1.0D)
println(Z(N-1)._2 - Z(N-1)._1)
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
Z((new scala.util.Random).nextInt(N)) = (1234L, 543.0d)
println(Z(N-1)._2 - Z(N-1)._1)
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def main(args: Array[String]): Unit = {
// Comment one or the other to look at the results
arrayOfTuples()
// twoParallelArrays()
}
}
我正在尝试评估哪种数据结构最能代表 Scala 中的稀疏向量。这些稀疏向量包含一个索引列表,每个索引都有一个值。我实施了一个小型基准测试,这似乎表明 Array[(Long, Double)]
似乎比 2 个并行数组占用的 space 少很多。那是对的吗?我是否正确地执行了该基准测试? (如果我在某处做错了,我不会感到惊讶)
import java.lang.management.ManagementFactory
import java.text.NumberFormat
object TestSize {
val N = 100000000
val formatter: NumberFormat = java.text.NumberFormat.getIntegerInstance
def twoParallelArrays(): Unit = {
val Z1 = Array.ofDim[Long](N)
val Z2 = Array.ofDim[Double](N)
Z1(N-1) = 1
Z2(N-1) = 1.0D
println(Z2(N-1) - Z1(N-1))
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def arrayOfTuples(): Unit = {
val Z = Array.ofDim[(Long, Double)](N)
Z(N-1) = (1, 1.0D)
println(Z(N-1)._2 - Z(N-1)._1)
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def main(args: Array[String]): Unit = {
// Comment one or the other to look at the results
//arrayOfTuples()
twoParallelArrays()
}
}
不,不正确。
Array.ofDim[(Long, Double)](N)
创建一个由 null
填充的大数组,并且不为 Long
、Double
或实际的 Tuple2
实例分配任何 space ,这就是为什么您在 heap-memory 用法中看不到任何内容。
two-array 版本立即为所有 Long
和 Double
分配它需要的所有 space,并且您会在堆 space 用法中看到它。
只需将 ofDim
替换为适当的 fill
即可查看 真实 数字。
在大小为 N = 1000000
的数组上:
arrayOfTuples: 45,693,312 19,190,296
twoParallelArrays: 25,925,792 19,315,256
arrayOfTuples
-解决方案显然需要更多 space。
您可能想知道为什么该系数大约为 1.8 而不是至少 2.5。这是因为Tuple2
对于一些原始数据类型来说是@specialized,特别是对于Long
和Double
,因此这两个8字节的原始数据可以存储在Tuple2
中没有拳击。因此,从数组到 Tuple2
的 64 位引用的总开销仅为 8 个字节,并且每个 Tuple2
实例中都有一些开销。但是,它不仅仅是将 Long
和 Double
直接存储在数组中。
顺便说一句:这正是 Apache Spark 使用所有这些 Encoder
存储数据的原因:这样您就不必担心元组和 case-classes.
完整代码:
import java.lang.management.ManagementFactory
import java.text.NumberFormat
object TestSize {
val N = 1000000
val formatter: NumberFormat = java.text.NumberFormat.getIntegerInstance
def twoParallelArrays(): Unit = {
val Z1 = Array.fill[Long](N)(42L)
val Z2 = Array.fill[Double](N)(42.0)
println(Z1)
println(Z2)
Z1(N-1) = 1
Z2(N-1) = 1.0D
println(Z2(N-1) - Z1(N-1))
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
Z1((new scala.util.Random).nextInt(N)) = 1234L
Z2((new scala.util.Random).nextInt(N)) = 345.0d
println(Z2(N-1) - Z1(N-1))
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def arrayOfTuples(): Unit = {
val Z = Array.fill[(Long, Double)](N)((42L, 42.0d))
Z(N-1) = (1, 1.0D)
println(Z(N-1)._2 - Z(N-1)._1)
val z1 = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage.getUsed
val z2 = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage.getUsed
Z((new scala.util.Random).nextInt(N)) = (1234L, 543.0d)
println(Z(N-1)._2 - Z(N-1)._1)
println(s"${formatter.format(z1)} ${formatter.format(z2)}")
}
def main(args: Array[String]): Unit = {
// Comment one or the other to look at the results
arrayOfTuples()
// twoParallelArrays()
}
}