2013-06-08 4 views
35

मैं जावा प्रोग्राम में स्कैला मैक्रोज़ के साथ कुछ कोड जनरेटिंग घटकों को प्रतिस्थापित कर रहा हूं, और व्यक्तिगत तरीकों के लिए जेनरेट बाइट कोड के आकार पर जावा वर्चुअल मशीन की सीमा में चल रहा हूं (64 किलोबाइट्स)।स्कैला मैक्रोज़ और जेवीएम की विधि आकार सीमा

उदाहरण के लिए, मान लें कि हमारे पास एक बड़ी-आईएसएम एक्सएमएल फ़ाइल है जो पूर्णांक से पूर्णांक तक मैपिंग का प्रतिनिधित्व करती है जिसे हम अपने कार्यक्रम में उपयोग करना चाहते हैं। हम रन टाइम पर इस फ़ाइल को पार्स से बचना चाहते हैं, तो हम उस मैक्रो संकलन समय पर पार्स करना होगा लिख ​​सकते हैं और हमारे विधि के शरीर बनाने के लिए फ़ाइल की सामग्री का उपयोग करेंगे:

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object BigMethod { 
    // For this simplified example we'll just make some data up. 
    val mapping = List.tabulate(7000)(i => (i, i + 1)) 

    def lookup(i: Int): Int = macro lookup_impl 
    def lookup_impl(c: Context)(i: c.Expr[Int]): c.Expr[Int] = { 
    import c.universe._ 

    val switch = reify(new scala.annotation.switch).tree 
    val cases = mapping map { 
     case (k, v) => CaseDef(c.literal(k).tree, EmptyTree, c.literal(v).tree) 
    } 

    c.Expr(Match(Annotated(switch, i.tree), cases)) 
    } 
} 

इस में यदि संकलित विधि आकार सीमा से अधिक हो, लेकिन यह कहने में एक अच्छी त्रुटि के बजाय, हमें TreePrinter.printSeq पर कई कॉल के साथ एक विशाल स्टैक ट्रेस दिया गया है और कहा जाता है कि हमने कंपाइलर को मार दिया है।

मेरे पास a solution है जिसमें मामलों को निश्चित आकार के समूहों में विभाजित करना, प्रत्येक समूह के लिए एक अलग विधि बनाना, और एक शीर्ष-स्तर का मिलान जोड़ना जो उपयुक्त समूह की विधि में इनपुट मान भेजता है। यह काम करता है, लेकिन यह अप्रिय है, और जब भी मैं एक मैक्रो लिखता हूं, तो मैं इस दृष्टिकोण का उपयोग नहीं करना चाहूंगा जहां जेनरेट कोड का आकार कुछ बाहरी संसाधनों पर निर्भर करता है।

क्या इस समस्या से निपटने के लिए एक क्लीनर तरीका है? सबसे महत्वपूर्ण बात यह है कि इस प्रकार की कंपाइलर त्रुटि से अधिक सावधानीपूर्वक निपटने का कोई तरीका है? मुझे लाइब्रेरी उपयोगकर्ता को एक अनजान समझने का विचार पसंद नहीं है "उस प्रविष्टि ने कंपाइलर को मार डाला है" त्रुटि संदेश सिर्फ इसलिए कि कुछ एक्सएमएल फ़ाइल जो मैक्रो द्वारा संसाधित की जा रही है, ने कुछ (काफी कम) आकार थ्रेसहोल्ड पार कर लिया है।

+1

इस सवाल के रूप में [ "पहले से ही उत्तर"] (http://stackoverflow.com/q/6570343/334519), लेकिन क्या मैं पूछ रहा हूँ क्या है कि में कहा जा रहा है से पूरी तरह अलग है चिह्नित किया गया है सवाल।मुझे पता है कि जेवीएम की विधि आकार सीमा को बदलना संभव नहीं है- मैं स्कैला के नए (2.10) मैक्रो सिस्टम के संदर्भ में वर्कअराउंड और त्रुटि प्रबंधन के बारे में पूछ रहा हूं। –

+0

नैतिक कोशिश की गई - ऑप्टिमाइज़ और 2.11 बस वहां बैठे बैठे। क्योंकि इस धरती पर मेरा समय सीमित है, मैं ctl-c'd। शायद यह मेरे लिए स्पष्ट हो जाएगा कि इसे बुरी तरह खत्म क्यों करना पड़ा। –

+0

@ सोम-स्नीट: दिलचस्प-यहां, और इसका कोई मतलब नहीं है इसका मतलब है। 'ऑप्टिमाइज़' के बिना, 2.11.0-एम 3 कम से कम एक उचित त्रुटि संदेश देता है। –

उत्तर

4

चूंकि किसी को कुछ कहना है, मैंने इसे वापस करने से पहले पेड़ को संकलित करने का प्रयास करने के लिए Importers पर निर्देशों का पालन किया।

यदि आप कंपाइलर को ढेर के बहुत सारे देते हैं, तो यह सही ढंग से त्रुटि की रिपोर्ट करेगा।

जहां ग्राहक कोड सिर्फ है के रूप में

[email protected]:~/tmp/bigmethod$ skalac -J-Xss1m biguser.scala 
Error is java.lang.StackOverflowError 
Error is java.lang.StackOverflowError 
biguser.scala:5: error: You ask too much of me. 
    Console println s"5 => ${BigMethod.lookup(5)}" 
             ^

करने का विरोध किया (यह पता है कि स्विच एनोटेशन, एक भविष्य व्यायाम के रूप में छोड़ दिया से कोई लेना देना नहीं मालूम था।)

[email protected]:~/tmp/bigmethod$ skalac bigmethod.scala ; skalac -J-Xss2m biguser.scala ; skala bigmethod.Test 
Error is java.lang.RuntimeException: Method code too large! 
Error is java.lang.RuntimeException: Method code too large! 
biguser.scala:5: error: You ask too much of me. 
    Console println s"5 => ${BigMethod.lookup(5)}" 
             ^
one error found 

कि:

package bigmethod 

object Test extends App { 
    Console println s"5 => ${BigMethod.lookup(5)}" 
} 

मेरे पहली बार इस API का उपयोग हूं, लेकिन अपने पिछले। मुझे किकस्टार्ट करने के लिए धन्यवाद।

package bigmethod 

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object BigMethod { 
    // For this simplified example we'll just make some data up. 
    //final val size = 700 
    final val size = 7000 
    val mapping = List.tabulate(size)(i => (i, i + 1)) 

    def lookup(i: Int): Int = macro lookup_impl 
    def lookup_impl(c: Context)(i: c.Expr[Int]): c.Expr[Int] = { 

    def compilable[T](x: c.Expr[T]): Boolean = { 
     import scala.reflect.runtime.{ universe => ru } 
     import scala.tools.reflect._ 
     //val mirror = ru.runtimeMirror(c.libraryClassLoader) 
     val mirror = ru.runtimeMirror(getClass.getClassLoader) 
     val toolbox = mirror.mkToolBox() 
     val importer0 = ru.mkImporter(c.universe) 
     type ruImporter = ru.Importer { val from: c.universe.type } 
     val importer = importer0.asInstanceOf[ruImporter] 
     val imported = importer.importTree(x.tree) 
     val tree = toolbox.resetAllAttrs(imported.duplicate) 
     try { 
     toolbox.compile(tree) 
     true 
     } catch { 
     case t: Throwable => 
      Console println s"Error is $t" 
      false 
     } 
    } 
    import c.universe._ 

    val switch = reify(new scala.annotation.switch).tree 
    val cases = mapping map { 
     case (k, v) => CaseDef(c.literal(k).tree, EmptyTree, c.literal(v).tree) 
    } 

    //val res = c.Expr(Match(Annotated(switch, i.tree), cases)) 
    val res = c.Expr(Match(i.tree, cases)) 

    // before returning a potentially huge tree, try compiling it 
    //import scala.tools.reflect._ 
    //val x = c.Expr[Int](c.resetAllAttrs(res.tree.duplicate)) 
    //val y = c.eval(x) 
    if (!compilable(res)) c.abort(c.enclosingPosition, "You ask too much of me.") 

    res 
    } 
} 
10

इमो डेटा को .class में डालने वास्तव में एक अच्छा विचार नहीं है। वे भी पार्स किए गए हैं, वे सिर्फ बाइनरी हैं। लेकिन उन्हें JVM में संग्रहीत करने से गारगेज कलेक्टर और जेआईटी कंपाइलर के प्रदर्शन पर नकारात्मक प्रभाव हो सकता है।

आपकी स्थिति में, मैं XML को उचित स्वरूप की बाइनरी फ़ाइल में पूर्व-संकलित करता हूं और उस पर विश्लेषण करता हूं। मौजूदा टूलिंग के साथ योग्य प्रारूप उदा। FastRPC या अच्छा पुराना DBF। उत्तरार्द्ध के कुछ कार्यान्वयन बुनियादी इंडेक्सिंग भी प्रदान कर सकते हैं जो पार्सिंग को छोड़ भी सकता है - ऐप सिर्फ संबंधित ऑफसेट से पढ़ेगा।

संबंधित मुद्दे