我不明白为什么下面的代码太慢了。这段代码的目标很简单:我有一组点,我想分成6个桶(每桶100000点)。代码:
Proving the simplification rules...
Proof failed.
1. ⋀x y xa evm1 A B used.
x (?x27 x y xa evm1 A B used) ⟶ y (?x27 x y xa evm1 A B used)
⟹ Says A B (packet A B Hi) ∈ used {x. y x}
⟶ Says A B (packet A B Hi) ∈ used {xa. x xa}
The error(s) above occurred for the goal statement:
mono (λp x. x = [] ∨
(∃evm1 A B used.
x = Says A B (packet A B Hi) # evm1 ∧
p evm1 ∧
A ≠ B ∧ Says A B (packet A B Hi) ∉ used {x. p x}))`
访问map并附加到列表缓冲区需要一个恒定的时间,所以对我来说,这段代码的复杂性是O(n),其中n是要分割的点数。我可以提出一些建议,使这段代码更快吗?
答案 0 :(得分:6)
以下重构不会产生与点数一样多的集合,并且依赖于Scala API,
object Main {
def main(args : Array[String]) = {
val labels = Array("1","2","3","4","5","6")
val points = Array.fill(600000){0.0}
val t1 = System.currentTimeMillis
val xst = points.grouped(labels.size).toArray.transpose
val m = (labels zip xst).toMap
val t2 = System.currentTimeMillis
println("fill values in = " + (t2-t1) + " msecs")
}
}
虽然原始代码需要几分钟,但这个需要大约700毫秒。
此代码可避免索引引用和更新现有集合。
使用我填充内存的代码更新(Alifirat)
object Main {
def main(args : Array[String]) = {
val labels = Array("1","2","3","4","5","6", "7")
val points = Array.fill(7000000){0.0}
val t1 = System.currentTimeMillis
val xst = points.grouped(labels.size).toArray.transpose
val m = (labels zip xst).toMap
val t2 = System.currentTimeMillis
println("fill values in = " + (t2-t1) + " msecs")
}
}
相同的代码,但7个桶的7,000 000点运行。
<强>更新强>
尝试
scala -J-Xmx4g
然后粘贴更新的代码。
<强>更新强>
如果最终地图映射到0.0
的数组,以下证明在7000万点上非常快,
val m = labels.map(l => l -> Array.fill(10*1000*1000){0.0}).toMap
如果性能至关重要,那么已经建议我使用面向C的方法来证明内存和时间效率,可能会牺牲可伸缩性和组合性。
答案 1 :(得分:2)
皮肤猫的另一种方式:
// some example values to be encoded
$secret = 'xxxyyy';
$key = 'some key';
$params = array('a', 'b', 'b');
// concatenate the values into a single string ready for encoding
$imploded = implode(',', $params); // becomes string 'a,b,c'
$concatenated_str = 'a,b,c' . ',' . $secret; // becomes 'a,b,c,xxxyyy'
// encode the string
$codice_sha1 = sha1($concatenated_str); // creates a SHA1 hash of the string
$codice = base64_encode("{$key}:{$codice_sha1}"); // creates base64 encoding of the SHA1 hash
我没有正确地对它进行基准测试,但它是我尝试过的最快的事情(不再是 - 请参阅我的其他答案)
答案 2 :(得分:1)
正如@Noah在评论中正确注意到的那样,您不必将缓冲区推回地图。这应该足够了:
val values = m.getOrElseUpdate(currentLabel, ListBuffer())
values += point
或者您可以使用功能方法来实现,如果您使用scala,则建议使用此方法:
val labels = Array("1","2","3","4","5","6")
val points = Array.fill(60000){2.0}
val t1 = System.currentTimeMillis
val m = points.zipWithIndex.groupBy {
case (point, i) => labels(i % labels.size)
}.mapValues(arr => arr.map(_._1).toList)
val t2 = System.currentTimeMillis
println(m)
println("fill values in = " + (t2-t1) + " msecs")
请注意 - 这里没有可变数据结构
答案 3 :(得分:1)
这里(作为一个单独的答案,因为它与我的第一个答案完全不同)是另一个“C风格”,它可以处理可变数量的桶。
有趣的是,在我的机器上,它的速度是Espen Brekke的另一个C风格答案的两倍。更新:我把它拿回来 - 两个都运行得如此之快,实际执行时间被启动时间掩盖等。运行100倍的点数(60000000),Espen的速度大约是其两倍(但具有固定数量的存储桶) - 大约220ms,而大约420ms
UPDATE2:Espen的最新版本现在或多或少与下面相同(除了使用异常而不是显式边界检查),毫不奇怪,它以相似的速度运行。
另请注意,这两个版本都不会创建存储区阵列。在我的机器上,这大约是另外200毫秒(对于任一版本),所以大约50%-100%的时间将东西放入桶中......
因此,此变体比OP的原始代码(在我的机器上)快约100,000倍!
TimerEvent.TIMER_COMPLETE
答案 4 :(得分:0)
有时,正确的方法是改变功能风格。 我用C风格的Scala编写了一个解决方案。
def main(args : Array[String]) = {
val labels = Array("1","2","3","4","5","6")
val points = Array.fill(600000){0.0}
val numChannels=6;
val channels=new Array[Array[Double]](numChannels);
for(i<-0 until numChannels){
channels(i)=new Array[Double]((points.length/6));
}
var from=0;
val t1 = System.currentTimeMillis
for(i<-0 until numChannels){
val channel=channels(i);
from=i
var to=0;
try{
while(true){
channel(to)=points(from);
to=to+1;
from=from+numChannels;
}
} catch {
case e:ArrayIndexOutOfBoundsException =>{
//Ok finished this channel
}
}
}
val t2 = System.currentTimeMillis
println("fill values in = " + (t2-t1) + " msecs")
println("from:"+from)
}
此解决方案仅执行需要完成的工作,而不再执行。
在我的mashine上使用8毫秒 榆树的代码使用309毫秒 原版花了221226毫秒。
虽然我喜欢Scala,但重要的是要记住它擅长隐藏其操作的计算成本。集合上的大多数操作至少为每个元素引入了几个过程调用。