স্কালার অলস ভালের দাম (লুকানো) কী?


165

স্কালার একটি সহজ বৈশিষ্ট্য হ'ল lazy valযেখানে কোনওটির মূল্যায়ন valপ্রয়োজনীয় না হওয়া অবধি দেরী হয় (প্রথম অ্যাক্সেসে)।

অবশ্যই, একটি lazy valকিছু ওভারহেড থাকতে হবে - কোথাও স্কালার অবশ্যই মূল্য নির্ধারণ করা হয়েছে এবং মূল্যায়নটি সিঙ্ক্রোনাইজ করা উচিত কিনা তা ট্র্যাক করে রাখতে হবে, কারণ একাধিক থ্রেড একই সময়ে প্রথমবারের জন্য মানটি অ্যাক্সেস করার চেষ্টা করতে পারে।

কি ঠিক একটি খরচ lazy valআছে লুকানো বুলিয়ান একটি সঙ্গে যুক্ত পতাকা - lazy valকি ঠিক সিঙ্ক্রোনাইজ এবং সেখানে কোন খরচ হয় ট্র্যাক রাখতে হলে মূল্যায়ন হয়েছে বা না?

এছাড়াও, ধরুন আমি এটি করি:

class Something {
    lazy val (x, y) = { ... }
}

এটি কি দুটি পৃথক পৃথক lazy valগুলি থাকা হিসাবে একই xএবং yআমি জুটির জন্য কেবল একবার ওভারহেড পাব (x, y)?

উত্তর:


86

এটি স্কেলা মেলিং তালিকা থেকে নেওয়া হয়েছে এবং lazyজাভা কোডের (বাইটোকোডের পরিবর্তে) শর্তাবলী প্রয়োগের বিশদ দেয় :

class LazyTest {
  lazy val msg = "Lazy"
}

নিম্নলিখিত জাভা কোডের সমতুল্য কিছুতে সংকলিত:

class LazyTest {
  public int bitmap$0;
  private String msg;

  public String msg() {
    if ((bitmap$0 & 1) == 0) {
        synchronized (this) {
            if ((bitmap$0 & 1) == 0) {
                synchronized (this) {
                    msg = "Lazy";
                }
            }
            bitmap$0 = bitmap$0 | 1;
        }
    }
    return msg;
  }

}

33
আমি মনে করি যে 2007 এ জাভা সংস্করণটি পোস্ট হওয়ার পরে বাস্তবায়নটি অবশ্যই বদলে গেছে one কেবলমাত্র একটি সিঙ্ক্রোনাইজড ব্লক রয়েছে এবং bitmap$0বর্তমান বাস্তবায়নে ক্ষেত্রটি অস্থিতিশীল (2.8))
মিচ ব্লিভিন্স

1
হ্যাঁ - আমি যা পোস্ট করছি তার প্রতি আমার আরও মনোযোগ দেওয়া উচিত ছিল!
অক্সবো_লেক্স

8
@ মিচ - আশা করি বাস্তবায়ন বদলে গেছে! ডাবল-চেক করা ইনিশিয়ালাইজেশন অ্যান্টি-প্যাটার্নটি একটি ক্লাসিক সূক্ষ্ম বাগ। দেখুন en.wikipedia.org/wiki/Double-checked_locking
Malvolio

20
এটি জাভা 1.4 পর্যন্ত অ্যান্টিপ্যাটার্ন ছিল। যেহেতু জাভা ১.২ ভোল্টাইল কীওয়ার্ডটির অর্থ কিছুটা কঠোর এবং এখন এই জাতীয় ডাবল চেকিং ঠিক আছে।
iirekm

8
সুতরাং, স্কেল ২.১০ হিসাবে বর্তমান বাস্তবায়ন কী? এছাড়াও, দয়া করে অনুগ্রহ করে কেউ কী বোঝাতে পারে যে ওভারহেডের অর্থ কতটা ওভারহেড এবং এর কিছুটা থাম্বের নিয়ম কখন ব্যবহার করা উচিত, কখন এড়ানো উচিত?
ইবি 84

39

দেখে মনে হচ্ছে সংকলকটি ক্লাস-লেভেল বিটম্যাপ ইন্টি ফিল্ডের জন্য একাধিক অলস ক্ষেত্রকে আরম্ভিত (বা না) হিসাবে পতাকাঙ্কিত করার জন্য ব্যবস্থা করে এবং বিটম্যাপের প্রাসঙ্গিক জোরটি যদি এটি প্রয়োজনীয় হয় তবে এটি একটি সিঙ্ক্রোনাইজড ব্লকে লক্ষ্য ক্ষেত্রটি আরম্ভ করে।

ব্যবহার:

class Something {
  lazy val foo = getFoo
  def getFoo = "foo!"
}

নমুনা বাইকোড উত্পাদন করে:

 0  aload_0 [this]
 1  getfield blevins.example.Something.bitmap$0 : int [15]
 4  iconst_1
 5  iand
 6  iconst_0
 7  if_icmpne 48
10  aload_0 [this]
11  dup
12  astore_1
13  monitorenter
14  aload_0 [this]
15  getfield blevins.example.Something.bitmap$0 : int [15]
18  iconst_1
19  iand
20  iconst_0
21  if_icmpne 42
24  aload_0 [this]
25  aload_0 [this]
26  invokevirtual blevins.example.Something.getFoo() : java.lang.String [18]
29  putfield blevins.example.Something.foo : java.lang.String [20]
32  aload_0 [this]
33  aload_0 [this]
34  getfield blevins.example.Something.bitmap$0 : int [15]
37  iconst_1
38  ior
39  putfield blevins.example.Something.bitmap$0 : int [15]
42  getstatic scala.runtime.BoxedUnit.UNIT : scala.runtime.BoxedUnit [26]
45  pop
46  aload_1
47  monitorexit
48  aload_0 [this]
49  getfield blevins.example.Something.foo : java.lang.String [20]
52  areturn
53  aload_1
54  monitorexit
55  athrow

টিউপলগুলিতে যেমন মূল্যবোধের সূচনা করা হয়েছিল lazy val (x,y) = { ... }একই ব্যবস্থার মাধ্যমে নেস্টেড ক্যাশে হয়েছে। টিউপল ফলাফলটি অলসভাবে মূল্যায়ন করা এবং ক্যাশে করা হয় এবং এক্স বা y এর যে কোনও একটি অ্যাক্সেস টিপল মূল্যায়নে ট্রিগার করবে। টিপল থেকে পৃথক মান উত্তোলন স্বাধীনভাবে এবং অলসভাবে করা হয় (এবং ক্যাশেড)। সুতরাং উপরে ডাবল ইনস্ট্যান্স কোড একটি উত্পন্ন x, y, এবং একটি x$1ধরনের ক্ষেত্র Tuple2


26

স্কেলা ২.১০ সহ একটি অলস মানের:

class Example {
  lazy val x = "Value";
}

বাইট কোডে সংকলিত যা নিম্নলিখিত জাভা কোডটির সাথে সাদৃশ্যপূর্ণ:

public class Example {

  private String x;
  private volatile boolean bitmap$0;

  public String x() {
    if(this.bitmap$0 == true) {
      return this.x;
    } else {
      return x$lzycompute();
    }
  }

  private String x$lzycompute() {
    synchronized(this) {
      if(this.bitmap$0 != true) {
        this.x = "Value";
        this.bitmap$0 = true;
      }
      return this.x;
    }
  }
}

নোট করুন যে বিটম্যাপটি একটি দ্বারা প্রতিনিধিত্ব করা হয়েছে boolean। আপনি অন্য ক্ষেত্র যুক্ত করে থাকেন, কম্পাইলার হিসেবে অন্তত 2 মূল্যবোধ, অর্থাত প্রতিনিধিত্ব করতে সক্ষম হচ্ছে মাঠের আকার বৃদ্ধি হবে byte। এটি কেবল বিশাল ক্লাসগুলির জন্য চলছে।

তবে আপনি ভাবতে পারেন কেন এটি কাজ করে? একটি সিঙ্ক্রোনাইজড ব্লকের মধ্যে প্রবেশের সময় থ্রেড-লোকাল ক্যাশেগুলি অবশ্যই সাফ করতে হবে যে অ-উদ্বায়ী xমানটি মেমোরিতে ফেলা হয়। এই ব্লগ নিবন্ধ একটি ব্যাখ্যা দেয় ।


11

স্কালা এসআইপি -20 অলস ভেলের একটি নতুন বাস্তবায়ন প্রস্তাব করেছে, যা আরও সঠিক তবে "বর্তমান" সংস্করণের চেয়ে 25% কম ধীর।

প্রস্তাবিত বাস্তবায়ন দেখে মনে হচ্ছে:

class LazyCellBase { // in a Java file - we need a public bitmap_0
  public static AtomicIntegerFieldUpdater<LazyCellBase> arfu_0 =
    AtomicIntegerFieldUpdater.newUpdater(LazyCellBase.class, "bitmap_0");
  public volatile int bitmap_0 = 0;
}
final class LazyCell extends LazyCellBase {
  import LazyCellBase._
  var value_0: Int = _
  @tailrec final def value(): Int = (arfu_0.get(this): @switch) match {
    case 0 =>
      if (arfu_0.compareAndSet(this, 0, 1)) {
        val result = 0
        value_0 = result
        @tailrec def complete(): Unit = (arfu_0.get(this): @switch) match {
          case 1 =>
            if (!arfu_0.compareAndSet(this, 1, 3)) complete()
          case 2 =>
            if (arfu_0.compareAndSet(this, 2, 3)) {
              synchronized { notifyAll() }
            } else complete()
        }
        complete()
        result
      } else value()
    case 1 =>
      arfu_0.compareAndSet(this, 1, 2)
      synchronized {
        while (arfu_0.get(this) != 3) wait()
      }
      value_0
    case 2 =>
      synchronized {
        while (arfu_0.get(this) != 3) wait()
      }
      value_0
    case 3 => value_0
  }
}

জুন ২০১৩ পর্যন্ত এই এসআইপি অনুমোদিত হয়নি। আমি আশা করি এটি মেলিং তালিকার আলোচনার ভিত্তিতে স্ক্যালার ভবিষ্যতের সংস্করণে অনুমোদিত এবং অন্তর্ভুক্ত হওয়ার সম্ভাবনা রয়েছে। ফলস্বরূপ, আমি মনে করি আপনি ড্যানিয়েল স্পিউকের পর্যবেক্ষণটি মনোযোগী হবেন :

অলস ভাল ভাল * মুক্ত নয় (বা এমনকি সস্তা)। নির্ভুলতার জন্য আপনার যদি একেবারে অলসতার প্রয়োজন হয় তবেই এটি ব্যবহার করুন, অপটিমাইজেশনের জন্য নয়।


10

এই সমস্যাটি সম্পর্কে আমি একটি পোস্ট লিখেছি https://dzone.com/articles/cost-laziness

সংক্ষেপে, জরিমানাটি এত কম যে অনুশীলনে আপনি এটিকে উপেক্ষা করতে পারেন।


1
এই মানদণ্ডের জন্য ধন্যবাদ। আপনি কি এসআইপি -20 প্রস্তাবিত বাস্তবায়নের বিরুদ্ধে মাপদণ্ড করতে পারেন?
তুরাদগ

-6

অলসতার জন্য স্কালার দ্বারা উত্পন্ন বাইকোডটি দেওয়া হলে, এটি ডাবল চেক লকিংয়ে উল্লিখিত হিসাবে থ্রেড সুরক্ষা সমস্যার সম্মুখীন হতে পারে http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html?page=1


3
এই দাবিটি মিচ দ্বারা গ্রহণযোগ্য উত্তরের একটি মন্তব্যেও করা হয়েছিল এবং @ আইরেকিএম খারিজ করেছেন: এই প্যাটার্নটি জাভা ১.৫ থেকে ঠিক আছে।
জেনস স্কাউডার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.