স্কালায় কীভাবে প্রোফাইল পদ্ধতিগুলি?


117

স্কালা পদ্ধতি কলগুলির প্রোফাইলিংয়ের একটি মানক উপায় কী?

আমার যা প্রয়োজন তা হ'ল একটি পদ্ধতির চারপাশে হুকগুলি যা ব্যবহার করে আমি টাইমারগুলি শুরু করতে এবং থামাতে ব্যবহার করতে পারি।

জাভাতে আমি পারস্পেক্ট প্রোগ্রামিং, অ্যাসপেক্টজে ব্যবহার করি, প্রোফাইল করার পদ্ধতিগুলি সংজ্ঞায়িত করতে এবং এটি অর্জনের জন্য বাইটকোড ইনজেকশন করুন।

স্কালায় আরও কি প্রাকৃতিক উপায় আছে, যেখানে আমি প্রক্রিয়াটিতে কোনও স্থির টাইপিং না হারিয়ে কোনও ফাংশনের আগে এবং পরে কল করার জন্য গুচ্ছ ফাংশন সংজ্ঞায়িত করতে পারি?


যদি অ্যাস্পেক্টজে স্কালার সাথে সুন্দরভাবে খেলতে থাকে তবে অ্যাসপেক্টজে ব্যবহার করুন। চাকা পুনরুদ্ধার কেন? কাস্টম ফ্লো কন্ট্রোল ব্যবহার করে উপরের উত্তরগুলি এওপির প্রাথমিক প্রয়োজনীয়তাগুলি অর্জন করতে ব্যর্থ হয়েছে কারণ সেগুলি ব্যবহার করার জন্য আপনার কোডটি পরিবর্তন করতে হবে। এগুলিও আগ্রহী হতে পারে: java.dzone.com/articles/real-world-scala-managing-cros blog.fakod.eu/2010/07/26/cross-cutting-concerns-in-scala
এন্ট


আপনি কিসে আগ্রহী? আপনি কি জানতে চান যে একটি নির্দিষ্ট পদ্ধতি উত্পাদন পরিবেশে কতক্ষণ সময় নেয়। তারপরে আপনার মেট্রিক্স লাইব্রেরিগুলি দেখতে হবে এবং স্বীকৃত উত্তরের মতো পরিমাপ রোল করবেন না। আপনি যদি আপনার বিকাশের পরিবেশে কোন কোড বৈকল্পিকটি "সাধারণভাবে" দ্রুত, তদন্ত করতে চান তবে নীচে উপস্থাপিত হিসাবে sbt-jmh ব্যবহার করুন।
জেএমজি

উত্তর:


214

আপনি যে কোডটির সময় নির্ধারণ করতে চান সেটি পরিবর্তন না করে আপনি কি এটি করতে চান? আপনি যদি কোডটি পরিবর্তন করতে আপত্তি করেন না, তবে আপনি এটির মতো কিছু করতে পারেন:

def time[R](block: => R): R = {
    val t0 = System.nanoTime()
    val result = block    // call-by-name
    val t1 = System.nanoTime()
    println("Elapsed time: " + (t1 - t0) + "ns")
    result
}

// Now wrap your method calls, for example change this...
val result = 1 to 1000 sum

// ... into this
val result = time { 1 to 1000 sum }

এটি ঝরঝরে, কোনও কোড পরিবর্তন না করে আমি কি একই কাজ করতে পারি?
শেখ

এই সমাধানের সাথে স্বয়ংক্রিয়ভাবে নয়; আপনি কী করতে চান তা স্কালা কীভাবে জানতে পারে?
জেস্পার

1
এটি কঠোরভাবে সত্য নয় - আপনি স্বয়ংক্রিয়ভাবে
REPL- এ

1
প্রায় নিখুঁত, তবে আপনাকে অবশ্যই সম্ভাব্য ব্যতিক্রমগুলিতে প্রতিক্রিয়া জানাতে হবে। t1একটি finallyঅনুচ্ছেদের মধ্যে গণনা করুন
জুলিয়ান

2
আপনি কিছু মুদ্রার সাথে আপনার প্রিন্টগুলিতে একটি লেবেল যুক্ত করতে পারেন: def time[R](label: String)(block: => R): R = {তারপরে লেবেলটি যুক্ত করুনprintln
গ্লেন 'দেবালিয়া'

34

জেস্পারের উত্তর ছাড়াও, আপনি স্বয়ংক্রিয়ভাবে আরআরপিএলে পদ্ধতিতে অনুরোধগুলি মোড়ানো করতে পারেন:

scala> def time[R](block: => R): R = {
   | val t0 = System.nanoTime()
   | val result = block
   | println("Elapsed time: " + (System.nanoTime - t0) + "ns")
   | result
   | }
time: [R](block: => R)R

এখন - এর মধ্যে কিছু মোড়ানো যাক

scala> :wrap time
wrap: no such command.  Type :help for help.

ঠিক আছে - আমাদের পাওয়ার মোডে থাকা দরকার

scala> :power
** Power User mode enabled - BEEP BOOP SPIZ **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._ and definitions._ also imported **
** Try  :help,  vals.<tab>,  power.<tab>    **

গুটিয়ে রাখা

scala> :wrap time
Set wrapper to 'time'

scala> BigDecimal("1.456")
Elapsed time: 950874ns
Elapsed time: 870589ns
Elapsed time: 902654ns
Elapsed time: 898372ns
Elapsed time: 1690250ns
res0: scala.math.BigDecimal = 1.456

সেই মুদ্রিত স্টাফটি কেন 5 বার আউট হয়েছে তা আমার কোনও ধারণা নেই

2.12.2 হিসাবে আপডেট করুন:

scala> :pa
// Entering paste mode (ctrl-D to finish)

package wrappers { object wrap { def apply[A](a: => A): A = { println("running...") ; a } }}

// Exiting paste mode, now interpreting.


scala> $intp.setExecutionWrapper("wrappers.wrap")

scala> 42
running...
res2: Int = 42

8
কাউকে এখনই ভাবতে না পারার ঝামেলা রক্ষা করার জন্য, :wrapফিচারটি আরপিএল থেকে সরানো হয়েছে: - s
চেস

25

আছে Scala জন্য তিনটি মাপকাঠিতে লাইব্রেরি যে আপনি পেতে পারেন।

যেহেতু লিঙ্কযুক্ত সাইটের ইউআরএলগুলি পরিবর্তন হওয়ার সম্ভাবনা রয়েছে তাই আমি নীচের প্রাসঙ্গিক বিষয়বস্তু আটক করছি।

  1. পারফরম্যান্স - পারফরম্যান্স টেস্টিং ফ্রেমওয়ার্কটি স্বয়ংক্রিয়ভাবে পারফরম্যান্স পরীক্ষার তুলনা এবং সিম্পল বিল্ড সরঞ্জামের মধ্যে কাজ করার লক্ষ্য।

  2. স্কেলা-বেঞ্চমার্কিং-টেম্পলেট - ক্যালিপারের উপর ভিত্তি করে স্কালা (মাইক্রো) বেঞ্চমার্ক তৈরির জন্য এসবিটি টেম্পলেট প্রকল্প।

  3. মেট্রিক্স - জেভিএম- এবং অ্যাপ্লিকেশন-স্তরের মেট্রিক্স ক্যাপচার করছে। সুতরাং আপনি কি জানেন যে কি চলছে


21

এটি আমি যা ব্যবহার করি:

import System.nanoTime
def profile[R](code: => R, t: Long = nanoTime) = (code, nanoTime - t)

// usage:
val (result, time) = profile { 
  /* block of code to be profiled*/ 
}

val (result2, time2) = profile methodToBeProfiled(foo)

6

testing.Benchmark দরকারী হতে পারে।

scala> def testMethod {Thread.sleep(100)}
testMethod: Unit

scala> object Test extends testing.Benchmark {
     |   def run = testMethod
     | }
defined module Test

scala> Test.main(Array("5"))
$line16.$read$$iw$$iw$Test$     100     100     100     100     100

5
সচেতন থাকুন যে পরীক্ষার জন্য B বেঞ্চমার্কটি @ প্রতীকৃত ("এই শ্রেণিটি সরানো হবে" "," ২.১০.০ ")।
Tvaroh

5

আমি জেস্পারের কাছ থেকে সমাধানটি নিয়েছি এবং একই কোডের একাধিক রানে এটিতে কিছু সংহতকরণ যুক্ত করেছি

def time[R](block: => R) = {
    def print_result(s: String, ns: Long) = {
      val formatter = java.text.NumberFormat.getIntegerInstance
      println("%-16s".format(s) + formatter.format(ns) + " ns")
    }

    var t0 = System.nanoTime()
    var result = block    // call-by-name
    var t1 = System.nanoTime()

    print_result("First Run", (t1 - t0))

    var lst = for (i <- 1 to 10) yield {
      t0 = System.nanoTime()
      result = block    // call-by-name
      t1 = System.nanoTime()
      print_result("Run #" + i, (t1 - t0))
      (t1 - t0).toLong
    }

    print_result("Max", lst.max)
    print_result("Min", lst.min)
    print_result("Avg", (lst.sum / lst.length))
}

ধরুন আপনি দুটি ফাংশন টাইম করতে চান counter_newএবং counter_old, নিম্নলিখিতটি হল:

scala> time {counter_new(lst)}
First Run       2,963,261,456 ns
Run #1          1,486,928,576 ns
Run #2          1,321,499,030 ns
Run #3          1,461,277,950 ns
Run #4          1,299,298,316 ns
Run #5          1,459,163,587 ns
Run #6          1,318,305,378 ns
Run #7          1,473,063,405 ns
Run #8          1,482,330,042 ns
Run #9          1,318,320,459 ns
Run #10         1,453,722,468 ns
Max             1,486,928,576 ns
Min             1,299,298,316 ns
Avg             1,407,390,921 ns

scala> time {counter_old(lst)}
First Run       444,795,051 ns
Run #1          1,455,528,106 ns
Run #2          586,305,699 ns
Run #3          2,085,802,554 ns
Run #4          579,028,408 ns
Run #5          582,701,806 ns
Run #6          403,933,518 ns
Run #7          562,429,973 ns
Run #8          572,927,876 ns
Run #9          570,280,691 ns
Run #10         580,869,246 ns
Max             2,085,802,554 ns
Min             403,933,518 ns
Avg             797,980,787 ns

আশা করি এটি সহায়ক


4

আমি এমন একটি প্রযুক্তি ব্যবহার করি যা কোড ব্লকে ঘুরে আসা সহজ। কর্কসটি হ'ল একই সঠিক লাইনটি টাইমার শুরু হয় এবং শেষ হয় - সুতরাং এটি সত্যিই একটি সাধারণ অনুলিপি এবং পেস্ট। অন্য দুর্দান্ত জিনিসটি হ'ল আপনি সেই একই লাইনে থাকা সময়টিকে স্ট্রিং হিসাবে কী বোঝায় তা নির্ধারণ করতে পারেন।

ব্যবহারের উদাহরণ:

Timelog("timer name/description")
//code to time
Timelog("timer name/description")

কোড:

object Timelog {

  val timers = scala.collection.mutable.Map.empty[String, Long]

  //
  // Usage: call once to start the timer, and once to stop it, using the same timer name parameter
  //
  def timer(timerName:String) = {
    if (timers contains timerName) {
      val output = s"$timerName took ${(System.nanoTime() - timers(timerName)) / 1000 / 1000} milliseconds"
      println(output) // or log, or send off to some performance db for analytics
    }
    else timers(timerName) = System.nanoTime()
  }

পেশাদাররা:

  • কোনও ব্লক হিসাবে কোড মোড়ানো বা লাইনের মধ্যে হেরফের করার দরকার নেই
  • অনুসন্ধানকারী হওয়ার সময় সহজেই কোড লাইনের মধ্যে টাইমারের শুরু এবং শেষ স্থানান্তর করতে পারে

কনস:

  • সম্পূর্ণ কার্যকরী কোডের জন্য কম চকচকে
  • স্পষ্টতই এই অবজেক্টটি ম্যাপের এন্ট্রি ফাঁস করে যদি আপনি টাইমারগুলি "বন্ধ" না করেন, যেমন যদি আপনার কোডটি একটি নির্দিষ্ট টাইমার শুরুর জন্য দ্বিতীয় অনুরোধে না আসে।

এটি দুর্দান্ত, তবে ব্যবহারটি কী হওয়া উচিত নয় Timelog.timer("timer name/description")?
শচুন

4

ScalaMeter বেঞ্চমার্কিং করার জন্য একটি দুর্দান্ত লাইব্রেরি

নীচে একটি সাধারণ উদাহরণ দেওয়া আছে

import org.scalameter._

def sumSegment(i: Long, j: Long): Long = (i to j) sum

val (a, b) = (1, 1000000000)

val execution_time = measure { sumSegment(a, b) }

আপনি যদি স্কাল ওয়ার্কশিটে উপরের কোড স্নিপেটটি কার্যকর করেন তবে আপনি চলমান সময়টি মিলি সেকেন্ডে পাবেন

execution_time: org.scalameter.Quantity[Double] = 0.260325 ms

3

আমি @ রিকের উত্তরটির সরলতা পছন্দ করি তবে এটিও চেয়েছিলাম:

  • প্রোফাইলার লুপিং পরিচালনা করে (ধারাবাহিকতা এবং সুবিধার জন্য)

  • আরও সঠিক সময় (ন্যানোটাইম ব্যবহার করে)

  • পুনরাবৃত্তির সময় (সমস্ত পুনরাবৃত্তির মোট সময় নয়)

  • শুধু এনএস / পুনরাবৃত্তি - একটি টিপল নয়

এটি এখানে অর্জন করা হয়:

def profile[R] (repeat :Int)(code: => R, t: Long = System.nanoTime) = { 
  (1 to repeat).foreach(i => code)
  (System.nanoTime - t)/repeat
}

আরও নির্ভুলতার জন্য, একটি সাধারণ পরিবর্তন কোনও ছোট স্নিপেটের সময় নির্ধারণের জন্য একটি জেভিএম হটস্পট ওয়ার্মআপ লুপকে (সময়সাপেক্ষে নয়) মঞ্জুরি দেয়:

def profile[R] (repeat :Int)(code: => R) = {  
  (1 to 10000).foreach(i => code)   // warmup
  val start = System.nanoTime
  (1 to repeat).foreach(i => code)
  (System.nanoTime - start)/repeat
}

এটি কোনও উত্তর নয়, মন্তব্য হিসাবে লিখতে ভাল হবে
নেডিম

1
@nedim সমাধানটি প্রশ্নের উত্তর দেওয়া হয় - আপনি যে কোনও সময় চাইলে একটি মোড়ক। ওপিকে যে কোনও ফাংশন কল করতে চাইবে তাকে মোড়কের মধ্যে বা তার ক্রিয়াকলাপগুলিতে কল করে এমন একটি ব্লক স্থাপন করা যেতে পারে যাতে তিনি "কোনও স্ট্যাটিক টাইপিং না হারিয়ে কোনও ফাংশনের আগে এবং পরে কল করার জন্য গুচ্ছ ফাংশন সংজ্ঞায়িত করতে পারেন"
ব্রেন্ট ফাউস্ট

1
তুমি ঠিক. দুঃখিত, আমি অবশ্যই কোডটি উপেক্ষা করেছি। আমার সম্পাদনা পর্যালোচনা হয়ে গেলে আমি ডাউনভোটটি পূর্বাবস্থায় ফেরাতে পারি।
নেডিম

3

স্ক্যান কোড বেঞ্চমার্কে প্রস্তাবিত পদ্ধতিটি এসবিটি-জেএমএইচ এর মাধ্যমে

"কারও উপর আস্থা রাখুন না, সব কিছু বেঞ্চ করুন।" - জেএমএইচ (জাভা মাইক্রোবেঞ্চমার্ক হারনেস) এর জন্য এসবিটি প্লাগইন

এই পন্থাটি অনেকগুলি বড় স্কাল প্রকল্প দ্বারা গৃহীত হয়েছে, উদাহরণস্বরূপ,

  • স্কালার প্রোগ্রামিং ভাষা নিজেই
  • ডটি (স্কেলা 3)
  • ফাংশনাল প্রোগ্রামিং জন্য বিড়াল গ্রন্থাগার
  • আইডিইগুলির জন্য ধাতব ভাষা সার্ভার

উপর ভিত্তি করে সরল মোড়কের টাইমার System.nanoTimeহয় একটি নির্ভরযোগ্য পদ্ধতি না মাপকাঠিতে এর:

System.nanoTimeString.internএখনকার মতো খারাপ : আপনি এটি ব্যবহার করতে পারেন, তবে এটি বিজ্ঞতার সাথে ব্যবহার করতে পারেন। টাইমারদের দ্বারা প্রবর্তিত বিলম্বিতা, গ্রানুলারিটি এবং স্কেলাবিলিটি প্রভাবগুলি যদি যথাযথ কঠোরতা ছাড়াই করা হয় তবে আপনার পরিমাপকে প্রভাবিত করতে পারে। System.nanoTimeবেঞ্চমার্ক ফ্রেমওয়ার্কের মাধ্যমে ব্যবহারকারীদের থেকে বিমূর্ত হওয়া উচিত এমন অনেকগুলি কারণ এটি

তদুপরি, জেআইটি ওয়ার্মআপ , আবর্জনা সংগ্রহ, সিস্টেম-ব্যাপী ইভেন্টগুলি ইত্যাদির মতো বিবেচনাগুলি পরিমাপের ক্ষেত্রে অনিশ্চয়তার পরিচয় দিতে পারে :

ওয়ার্মআপ, ডেড কোড নির্মূলকরণ, কাঁটাচামচ ইত্যাদিসহ প্রচুর পরিমাণে প্রভাব হ্রাস করা দরকার সৌভাগ্যক্রমে, জেএমএইচ ইতিমধ্যে অনেক কিছুর যত্ন নিয়েছে, এবং জাভা এবং স্কালা উভয়ের জন্যই বন্ডিং রয়েছে।

ট্র্যাভিস ব্রাউন এর উত্তরের ভিত্তিতে এখানে স্কালার জন্য জেএমএইচ বেঞ্চমার্ক কীভাবে সেটআপ করবেন তার একটি উদাহরণ

  1. Jmh এ যুক্ত করুন project/plugins.sbt
    addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
  2. এতে jmh প্লাগইন সক্ষম করুন build.sbt
    enablePlugins(JmhPlugin)
  3. যোগ করা src/main/scala/bench/VectorAppendVsListPreppendAndReverse.scala

    package bench
    
    import org.openjdk.jmh.annotations._
    
    @State(Scope.Benchmark)
    @BenchmarkMode(Array(Mode.AverageTime))
    class VectorAppendVsListPreppendAndReverse {
      val size = 1_000_000
      val input = 1 to size
    
      @Benchmark def vectorAppend: Vector[Int] = 
        input.foldLeft(Vector.empty[Int])({ case (acc, next) => acc.appended(next)})
    
      @Benchmark def listPrependAndReverse: List[Int] = 
        input.foldLeft(List.empty[Int])({ case (acc, next) => acc.prepended(next)}).reverse
    }
  4. এর সাথে বেঞ্চমার্ক কার্যকর করুন
    sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 bench.VectorAppendVsListPreppendAndReverse"

ফলাফল হয়

Benchmark                                                   Mode  Cnt  Score   Error  Units
VectorAppendVsListPreppendAndReverse.listPrependAndReverse  avgt   20  0.024 ± 0.001   s/op
VectorAppendVsListPreppendAndReverse.vectorAppend           avgt   20  0.130 ± 0.003   s/op

যা কোনওটিকে প্রেন্ডিং করে Listএবং তারপরে শেষ দিকে উল্টানো ইঙ্গিত করে বলে মনে হয় এটি একটিতে যুক্ত হওয়ার চেয়ে দ্রুতগতির ক্রম Vector


1

দৈত্যদের কাঁধে দাঁড়িয়ে থাকার সময় ...

একটি শক্তিশালী তৃতীয় পক্ষের গ্রন্থাগারটি আরও আদর্শ হতে পারে তবে আপনার যদি দ্রুত এবং স্টাডি-লাইব্রেরি ভিত্তিক কিছু প্রয়োজন হয় তবে নিম্নলিখিত বৈকল্পিকটি সরবরাহ করে:

  • repetitions
  • শেষ ফলাফল জয় একাধিক পুনরাবৃত্তির জন্য
  • একাধিক পুনরাবৃত্তির জন্য মোট সময় এবং গড় সময়
  • পরম হিসাবে সময় / তাত্ক্ষণিক সরবরাহকারীর প্রয়োজন সরিয়ে দেয়

import scala.concurrent.duration._
import scala.language.{postfixOps, implicitConversions}

package object profile {

  def profile[R](code: => R): R = profileR(1)(code)

  def profileR[R](repeat: Int)(code: => R): R = {
    require(repeat > 0, "Profile: at least 1 repetition required")

    val start = Deadline.now

    val result = (1 until repeat).foldLeft(code) { (_: R, _: Int) => code }

    val end = Deadline.now

    val elapsed = ((end - start) / repeat)

    if (repeat > 1) {
      println(s"Elapsed time: $elapsed averaged over $repeat repetitions; Total elapsed time")

      val totalElapsed = (end - start)

      println(s"Total elapsed time: $totalElapsed")
    }
    else println(s"Elapsed time: $elapsed")

    result
  }
}

এছাড়াও লক্ষণীয় যে আপনি Duration.toCoarsestসম্ভাব্য বৃহত্তম সময় ইউনিটে রূপান্তর করতে পদ্ধতিটি ব্যবহার করতে পারেন , যদিও আমি নিশ্চিত নই যে এটি রানের মধ্যে সামান্য সময়ের ব্যবধানের সাথে কতটা বন্ধুত্বপূর্ণ eg

Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.concurrent.duration._
import scala.concurrent.duration._

scala> import scala.language.{postfixOps, implicitConversions}
import scala.language.{postfixOps, implicitConversions}

scala> 1000.millis
res0: scala.concurrent.duration.FiniteDuration = 1000 milliseconds

scala> 1000.millis.toCoarsest
res1: scala.concurrent.duration.Duration = 1 second

scala> 1001.millis.toCoarsest
res2: scala.concurrent.duration.Duration = 1001 milliseconds

scala> 

1

আপনি ব্যবহার করতে পারেন System.currentTimeMillis:

def time[R](block: => R): R = {
    val t0 = System.currentTimeMillis()
    val result = block    // call-by-name
    val t1 = System.currentTimeMillis()
    println("Elapsed time: " + (t1 - t0) + "ms")
    result
}

ব্যবহার:

time{
    //execute somethings here, like methods, or some codes.
}  

ন্যানোটাইম আপনাকে দেখাবে ns, তাই এটি দেখতে শক্ত হবে। সুতরাং আমি পরামর্শ দিচ্ছি যে আপনি এটির পরিবর্তে কারেন্টটাইমমিলিস ব্যবহার করতে পারেন।


ন্যানোসেকেন্ডগুলি দেখতে অসুবিধা হওয়াই দুজনের মধ্যে বাছাইয়ের একটি দুর্বল কারণ। রেজোলিউশন ছাড়াও কিছু গুরুত্বপূর্ণ পার্থক্য রয়েছে। একটির জন্য, ওএস পর্যায়ক্রমে সম্পাদিত ক্লক অ্যাডজাস্টমেন্টের সময় কারেন্টটাইমমিলিস পরিবর্তন করতে এবং এমনকি পিছনের দিকে যেতে পারে। আরেকটি হ'ল ন্যানোটাইম থ্রেড নিরাপদ নাও হতে পারে: stackoverflow.com/questions/351565/…
ক্রিস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.