2016-06-18 5 views
6

я столкнулся с этой проблемой при выполнении домашних заданий от Coursera «лестница специализации» (это упрощенная версию и не содержит какие-либо деталей домашнего задания, это просто обход массива)Scala сопоставления с образцом производительность

val chars: Array[Char] = some array 

def fun1(idx:Int):Int = { 
    some code here (including the stop condition) 

    val c = chars(idx) 
    c match{ 
     case '(' => fun1(idx+1) 
     case _ => fun1(idx+1) 
    } 

} 

Этого код 4 раз медленнее, чем

def fun2(idx: Int):Int = { 
    some code here (including the stop condition) 

    val c = chars(idx) 
    (c == '(') match{ 
     case true => fun2(idx+1) 
     case _ => fun2(idx+1) 
    } 
} 

Все, что я делаю меняется по шаблону (Я бегу это с помощью ScalMeter поэтому я считаю, в статистике).

Может ли кто-нибудь объяснить это поведение?

ответ

2

Я могу только подтвердить, что первый match ~ 50% медленнее, а не 4x (2.11.8). В любом случае, если вы посмотрите на байт-код, вы можете обнаружить, что первый match переведен на инструкцию tableswitch, которая обычно используется для оператора Java switch с несколькими вариантами выбора и в основном является поиском goto, а вторая переведена на if. Таким образом, второй match просто:

if (c == '(') fun2(idx+1) else fun2(idx+1) 
0

Update ниже неправильно (большинство времени в этих испытаниях было потрачено генерации данных, поэтому разница в фактическое время обхода не было заметно Запустив эту же точку отсчета. с постоянным входом показывает ~ 125 мс на 100 миллионов записей для case ')' случай против ~ 35 мс для другого случая.)

Я не вижу разницы, которую вы описываете. Не знаю, как ScalaMeter это делает, но запустить его в РЕПЛЕ (после того, как дать «разогреть», запустив «сухие» несколько раз), я получаю практически такую ​​же производительность:

def func(s: Seq[Char], idx: Int): String = 
    if(idx == s.length) "foo" else s(idx) match { 
    case ')' => func(s, idx+1) 
    case _ => func(s, idx+1) 
    } 

def func1(s: Seq[Char], idx: Int): String = 
    if(idx == s.length) "foo" else (s(idx) == '(') match { 
    case true => func(s, idx+1) 
    case _ => func(s, idx+1) 
    } 

import scala.util.Random 
def randy = Stream.continually(Random.nextPrintableChar) 


def doit(n: Int)(f: (Seq[Char], Int) => String) = { 
val start = System.currentTimeMillis;  
f(randy.take(n).toIndexedSeq, 0); 
System.currentTimeMillis - start 
} 


scala> doit(1000000)(func) 
res9: Long = 231 

scala> doit(1000000)(func1) 
res10: Long = 238 

scala> doit(1000000)(func) 
res11: Long = 234 

scala> doit(1000000)(func1) 
res12: Long = 201 

и т.д. Как вы можете видеть, нет существенной разницы.

+0

Я бы сомневался, что это что-то около действующего метода бенчмаркинга. ScalaMeter любит пару десятков разминок до тех пор, пока результаты не стабилизируются. Вы даже не проверяете использование одних и тех же данных. –

+0

Да, не те же данные. Но я получаю довольно близкие результаты между прогонами. Stddev крайне низок, что предполагает, что если бы я использовал одни и те же данные, я бы не видел большой разницы. Как уже упоминалось в ответе, я сделал разминки, поэтому не уверен, что с этим эталоном вы найдете «недействительным». Я согласен с моими выводами и призываю вас опровергнуть их окончательно (и воспроизводимо), если сможете. – Dima

+0

Заимствование ваших функций, вот эталонный показатель с scala-счетчиком: https://gist.github.com/lukaszwawrzyk/a2505d5b3083bb72de51b8445fbb9a76 Предоставление 'char time: 13.172258374999998 ms' и' bool time: 4.739404575 ms'. Это делается для массивов, как в вопросе, если использование индексированных результатов seq действительно ближе, но не равно (95 секунд против 80 секунд) –

Смежные вопросы