CSS @property-এর কর্মক্ষমতা বেঞ্চমার্ক করা

প্রকাশিত: 2 অক্টোবর, 2024

একটি নতুন CSS বৈশিষ্ট্য ব্যবহার করা শুরু করার সময় আপনার ওয়েবসাইটের কর্মক্ষমতার উপর এর প্রভাব বোঝা গুরুত্বপূর্ণ, তা ইতিবাচক বা নেতিবাচক। @property সাথে এখন বেসলাইনে এই পোস্টটি এর পারফরম্যান্সের প্রভাব এবং নেতিবাচক প্রভাব প্রতিরোধে আপনি যা করতে পারেন তা অন্বেষণ করে।

PerfTestRunner দিয়ে CSS-এর কর্মক্ষমতা বেঞ্চমার্ক করা

CSS-এর কর্মক্ষমতা বেঞ্চমার্ক করার জন্য আমরা "CSS সিলেক্টর বেঞ্চমার্ক" টেস্ট স্যুট তৈরি করেছি। এটি Chromium-এর PerfTestRunner দ্বারা চালিত এবং CSS-এর কার্যক্ষমতার প্রভাবকে বেঞ্চমার্ক করে৷ এই PerfTestRunner যা Blink–Chromium-এর অন্তর্নিহিত রেন্ডারিং ইঞ্জিন–এর অভ্যন্তরীণ কর্মক্ষমতা পরীক্ষার জন্য ব্যবহার করে৷

রানার একটি measureRunsPerSecond পদ্ধতি অন্তর্ভুক্ত করে যা পরীক্ষার জন্য ব্যবহৃত হয়। প্রতি সেকেন্ডে রানের সংখ্যা যত বেশি হবে তত ভালো। এই লাইব্রেরির সাথে একটি মৌলিক measureRunsPerSecond -বেঞ্চমার্ক এইরকম দেখায়:

const testResults = PerfTestRunner.measureRunsPerSecond({
 
"Test Description",
 
iterationCount: 5,
 
bootstrap: function() {
   
// Code to execute before all iterations run
   
// For example, you can inject a style sheet here
 
},
 
setup: function() {
   
// Code to execute before a single iteration
 
},
 
run: function() {
   
// The actual test that gets run and measured.
   
// A typical test adjusts something on the page causing a style or layout invalidation
 
},
 
tearDown: function() {
   
// Code to execute after a single iteration has finished
   
// For example, undo DOM adjustments made within run()
 
},
 
done: function() {
   
// Code to be run after all iterations have finished.
   
// For example, remove the style sheets that were injected in the bootstrap phase
 
},
});

measureRunsPerSecond প্রতিটি বিকল্প কোড ব্লকে মন্তব্যের মাধ্যমে বর্ণনা করা হয়েছে, run ফাংশনটি মূল অংশ যা পরিমাপ করা হয়।

CSS নির্বাচক বেঞ্চমার্কের জন্য একটি DOM ট্রি প্রয়োজন

যেহেতু CSS নির্বাচকদের কর্মক্ষমতাও DOM এর আকারের উপর নির্ভর করে, এই মানদণ্ডের জন্য একটি শালীন আকারের DOM ট্রি প্রয়োজন। ম্যানুয়ালি এই DOM গাছটি তৈরি করার পরিবর্তে, এই গাছটি তৈরি হয়।

উদাহরণস্বরূপ, নিম্নলিখিত makeTree ফাংশনটি @property বেঞ্চমার্কের অংশ। এটি 1000টি উপাদানের একটি গাছ তৈরি করে, প্রতিটি উপাদানের ভিতরে কিছু শিশু বাসা বাঁধে।

const $container = document.querySelector('#container');

function makeTree(parentEl, numSiblings) {
 
for (var i = 0; i <= numSiblings; i++) {
    $container
.appendChild(
      createElement
('div', {
        className
: `tagDiv wrap${i}`,
        innerHTML
: `<div class="tagDiv layer1" data-div="layer1">
         
<div class="tagDiv layer2">
           
<ul class="tagUl">
             
<li class="tagLi"><b class="tagB"><a href="/" class="tagA link" data-select="link">Select</a></b></li>
           
</ul>
          </
div>
       
</div>`,
     
})
   
);
 
}
}

makeTree
($container, 1000);

যেহেতু CSS নির্বাচক বেঞ্চমার্কগুলি DOM ট্রি পরিবর্তন করে না, এই ট্রি জেনারেশনটি শুধুমাত্র একবার কার্যকর করা হয়, যেকোনও বেঞ্চমার্ক চালানোর আগে।

একটি বেঞ্চমার্ক চলমান

টেস্ট স্যুটের অংশ এমন একটি বেঞ্চমার্ক চালানোর জন্য আপনাকে প্রথমে একটি ওয়েব সার্ভার শুরু করতে হবে:

npm run start

একবার শুরু হলে আপনি এর প্রকাশিত URL-এ বেঞ্চমার্ক দেখতে পারেন এবং window.startTest() ম্যানুয়ালি চালাতে পারেন।

এই বেঞ্চমার্কগুলিকে বিচ্ছিন্নভাবে চালানোর জন্য-কোনও এক্সটেনশন বা অন্যান্য কারণের হস্তক্ষেপ ছাড়াই- বেঞ্চমার্কে পাস করা লোড এবং কার্যকর করতে CLI থেকে Puppeteer ট্রিগার করা হয়।

এই @property বেঞ্চমার্কের জন্য বিশেষভাবে এর URL-এ প্রাসঙ্গিক পৃষ্ঠা দেখার পরিবর্তে http://localhost:3000/benchmarks/at-rule/at-property.html CLI-তে নিম্নলিখিত কমান্ডগুলি ব্যবহার করুন:

npm run benchmark at-rule/at-property

এটি Puppeteer-এর মাধ্যমে পৃষ্ঠাটি লোড করে, স্বয়ংক্রিয়ভাবে window.startTest() কে কল করে, এবং ফলাফলগুলি ফেরত দেয়।

CSS বৈশিষ্ট্যের কর্মক্ষমতা বেঞ্চমার্কিং

একটি CSS প্রপার্টির পারফরম্যান্স বেঞ্চমার্ক করার জন্য, আপনি বেঞ্চমার্ক করুন কত দ্রুত এটি একটি স্টাইল অবৈধকরণ এবং পরবর্তীতে পুনরায় গণনা করার শৈলী কাজটি ব্রাউজারকে করতে হবে।

শৈলী অবৈধকরণ হল চিহ্নিত করার প্রক্রিয়া যা DOM-এ পরিবর্তনের প্রতিক্রিয়া হিসাবে তাদের শৈলীর পুনঃগণনা করা প্রয়োজন। সহজতম সম্ভাব্য পদ্ধতি হল প্রতিটি পরিবর্তনের প্রতিক্রিয়া হিসাবে সবকিছু বাতিল করা।

এটি করার সময়, উত্তরাধিকারসূত্রে পাওয়া CSS বৈশিষ্ট্য এবং উত্তরাধিকারসূত্রে পাওয়া CSS বৈশিষ্ট্যগুলির মধ্যে পার্থক্য করতে হবে।

  • যখন একটি CSS প্রপার্টি যা উত্তরাধিকারসূত্রে একটি লক্ষ্যবস্তুতে পরিবর্তিত হয়, তখন লক্ষ্যবস্তু উপাদানের নীচে সাবট্রিতে সম্ভাব্য সব উপাদানের শৈলীও পরিবর্তন করতে হবে।
  • যখন একটি CSS প্রপার্টি যা উত্তরাধিকারসূত্রে একটি টার্গেটেড এলিমেন্টে পরিবর্তন করে না, তখন শুধুমাত্র সেই স্বতন্ত্র এলিমেন্টের স্টাইলগুলি অবৈধ হয়ে যায়।

কারণ যে বৈশিষ্ট্যগুলি উত্তরাধিকারসূত্রে পাওয়া যায় না এমন বৈশিষ্ট্যগুলির সাথে তুলনা করা ন্যায়সঙ্গত হবে না, চালানোর জন্য দুটি বেঞ্চমার্ক রয়েছে:

  • উত্তরাধিকারসূত্রে পাওয়া বৈশিষ্ট্য সহ মানদণ্ডের একটি সেট।
  • এমন বৈশিষ্ট্য সহ মানদণ্ডের একটি সেট যা উত্তরাধিকারসূত্রে পাওয়া যায় না।

কোন বৈশিষ্ট্যগুলিকে বেঞ্চমার্ক করতে হবে তা সাবধানে বেছে নেওয়া গুরুত্বপূর্ণ৷ যদিও কিছু বৈশিষ্ট্য (যেমন accent-color ) শুধুমাত্র শৈলীকে বাতিল করে, সেখানে অনেক বৈশিষ্ট্য (যেমন writing-mode ) রয়েছে যা লেআউট বা পেইন্টের মতো অন্যান্য জিনিসকেও বাতিল করে। আপনি এমন বৈশিষ্ট্যগুলি চান যা শুধুমাত্র শৈলীগুলিকে অবৈধ করে।

এটি নির্ধারণ করতে, Blink-এর CSS বৈশিষ্ট্যের তালিকায় জিনিসগুলি দেখুন। প্রতিটি সম্পত্তির একটি ক্ষেত্র invalidate আছে যা তালিকাভুক্ত করে যা অবৈধ হয়ে যায়।

তদুপরি, সেই তালিকা থেকে independent হিসাবে চিহ্নিত নয় এমন একটি সম্পত্তি বাছাই করাও গুরুত্বপূর্ণ, কারণ এই জাতীয় সম্পত্তির বেঞ্চমার্ক করা ফলাফলগুলিকে তির্যক করবে। স্বাধীন বৈশিষ্ট্যের অন্যান্য বৈশিষ্ট্য বা পতাকার উপর কোন পার্শ্বপ্রতিক্রিয়া নেই। যখন শুধুমাত্র স্বাধীন বৈশিষ্ট্যগুলি পরিবর্তিত হয়, তখন ব্লিঙ্ক একটি দ্রুত কোড-পথ ব্যবহার করে যা বংশধরের শৈলীকে ক্লোন করে এবং সেই ক্লোন করা অনুলিপিতে নতুন মান আপডেট করে। এই পদ্ধতিটি সম্পূর্ণ পুনঃগণনা করার চেয়ে দ্রুত।

উত্তরাধিকারসূত্রে পাওয়া CSS বৈশিষ্ট্যের কর্মক্ষমতা বেঞ্চমার্ক করা

বেঞ্চমার্কের প্রথম সেটটি উত্তরাধিকারসূত্রে পাওয়া CSS বৈশিষ্ট্যের উপর ফোকাস করে। তিনটি ধরণের বৈশিষ্ট্য রয়েছে যা একে অপরের বিরুদ্ধে পরীক্ষা এবং তুলনা করার উত্তরাধিকারসূত্রে পাওয়া যায়:

  • একটি নিয়মিত সম্পত্তি যা উত্তরাধিকারসূত্রে পাওয়া যায়: accent-color
  • একটি অনিবন্ধিত কাস্টম সম্পত্তি: --unregistered
  • একটি কাস্টম সম্পত্তি যা inherits: true : --registered .

অনিবন্ধিত কাস্টম বৈশিষ্ট্য এই তালিকায় যোগ করা হয়েছে কারণ এইগুলি ডিফল্টরূপে উত্তরাধিকারী হয়।

পূর্বে উল্লিখিত হিসাবে, উত্তরাধিকারসূত্রে প্রাপ্ত সম্পত্তিটি সাবধানে বেছে নেওয়া হয়েছিল যাতে এটি এমন একটি যা শুধুমাত্র শৈলীগুলিকে বাতিল করে এবং একটি যা independent হিসাবে চিহ্নিত করা হয় না।

নিবন্ধিত কাস্টম বৈশিষ্ট্যগুলির জন্য, শুধুমাত্র inherits বর্ণনাকারী সত্যে সেট করা এই রানে পরীক্ষা করা হয়। inherits বর্ণনাকারী নির্ধারণ করে যে সম্পত্তিটি উত্তরাধিকারসূত্রে সন্তানদের কাছে আসে কি না। এই সম্পত্তিটি CSS @property বা JavaScript CSS.registerProperty মাধ্যমে নিবন্ধিত কিনা তা বিবেচ্য নয়, কারণ নিবন্ধন নিজেই বেঞ্চমার্কের অংশ নয়।

বেঞ্চমার্ক

ইতিমধ্যেই উল্লেখ করা হয়েছে, যে পৃষ্ঠাটিতে মানদণ্ড রয়েছে সেটি একটি DOM ট্রি তৈরি করে শুরু হয় যাতে পৃষ্ঠাটিতে নোডের একটি বড় সেট থাকে যাতে পরিবর্তনের কোনো প্রভাব দেখা যায়।

প্রতিটি বেঞ্চমার্ক একটি সম্পত্তির মান পরিবর্তন করে যার পরে এটি একটি শৈলী অবৈধতা ট্রিগার করে। বেঞ্চমার্ক মূলত পরিমাপ করে যে পৃষ্ঠাটির পরবর্তী পুনঃগণনা সেই সমস্ত অবৈধ শৈলীগুলিকে পুনরায় মূল্যায়ন করতে কতক্ষণ সময় নেয়।

একটি একক বেঞ্চমার্ক সম্পন্ন হওয়ার পরে, যেকোনো ইনজেকশনের শৈলী পুনরায় সেট করা হয় যাতে পরবর্তী বেঞ্চমার্ক শুরু হতে পারে।

উদাহরণ স্বরূপ, --registered এর শৈলী পরিবর্তনের কর্মক্ষমতা পরিমাপ করা বেঞ্চমার্ক এই রকম দেখাচ্ছে:

let i = 0;
PerfTestRunner.measureRunsPerSecond({
  description
,
  iterationCount
: 5,
  bootstrap
: () => {
    setCSS
(`@property --registered {
      syntax
: "<number>";
      initial
-value: 0;
      inherits
: true;
   
}`);
 
},
  setup
: function() {
   
// NO-OP
 
},
  run
: function() {
    document
.documentElement.style.setProperty('--registered', i);
    window
.getComputedStyle(document.documentElement).getPropertyValue('--registered'); // Force style recalculation
    i
= (i == 0) ? 1 : 0;
 
},
  teardown
: () => {
    document
.documentElement.style.removeProperty('--registered');
 
},
  done
: (results) => {
    resetCSS
();
    resolve
(results);
 
},
});

অন্যান্য ধরণের বৈশিষ্ট্যগুলি পরীক্ষা করে এমন বেঞ্চমার্কগুলি একইভাবে কাজ করে তবে একটি খালি bootstrap রয়েছে কারণ নিবন্ধনের জন্য কোনও সম্পত্তি নেই।

ফলাফল

16GB RAM সহ 2021 MacBook Pro (Apple M1 Pro) তে 20 পুনরাবৃত্তি সহ এই বেঞ্চমার্কগুলি চালানোর ফলে নিম্নলিখিত গড় পাওয়া যায়:

  • নিয়মিত সম্পত্তি যা উত্তরাধিকারসূত্রে পাওয়া যায় ( accent-color ): 163 রান প্রতি সেকেন্ডে (= প্রতি রান 6.13ms)
  • অনিবন্ধিত কাস্টম সম্পত্তি ( --unregistered ): প্রতি সেকেন্ডে 256 রান (= প্রতি রান 3.90ms)
  • inherits: true ( --registered ): প্রতি সেকেন্ডে 252 রান (= রান প্রতি 3.96ms)

একাধিক রানে, বেঞ্চমার্ক একই রকম ফলাফল দেয়।

উত্তরাধিকারসূত্রে পাওয়া সম্পত্তির ফলাফল সহ বার চার্ট। উচ্চ সংখ্যা দ্রুত সঞ্চালন.
চিত্র: উত্তরাধিকারসূত্রে পাওয়া বৈশিষ্ট্যগুলির ফলাফল সহ বার চার্ট। উচ্চ সংখ্যা দ্রুত সঞ্চালন.

ফলাফলগুলি দেখায় যে কাস্টম সম্পত্তি নিবন্ধন না করার তুলনায় একটি কাস্টম সম্পত্তি নিবন্ধন করা খুব অল্প খরচে আসে। নিবন্ধিত কাস্টম বৈশিষ্ট্য যা উত্তরাধিকার সূত্রে প্রাপ্ত হয় অনিবন্ধিত কাস্টম বৈশিষ্ট্যগুলির গতির 98% গতিতে চলে। পরম সংখ্যায়, কাস্টম সম্পত্তি নিবন্ধন করলে একটি 0.06ms ওভারহেড যোগ হয়।

উত্তরাধিকারসূত্রে পাওয়া যায় না এমন CSS বৈশিষ্ট্যের কর্মক্ষমতা বেঞ্চমার্ক করা

বেঞ্চমার্কের পরবর্তী বৈশিষ্ট্যগুলি হল যেগুলি উত্তরাধিকারী নয়৷ এখানে কেবলমাত্র দুটি ধরণের বৈশিষ্ট্য রয়েছে যা বেঞ্চমার্ক করা যেতে পারে:

  • একটি নিয়মিত সম্পত্তি যা উত্তরাধিকারী হয় না: z-index
  • inherits: false : --registered-no-inherit

নিবন্ধিত নয় এমন কাস্টম বৈশিষ্ট্যগুলি এই বেঞ্চমার্কের অংশ হতে পারে না কারণ সেই বৈশিষ্ট্যগুলি সর্বদা উত্তরাধিকারসূত্রে পাওয়া যায়৷

বেঞ্চমার্ক

বেঞ্চমার্কগুলি পূর্ববর্তী পরিস্থিতিগুলির সাথে খুব মিল। --registered-no-inherit এর সাথে পরীক্ষার জন্য, নিম্নোক্ত সম্পত্তি নিবন্ধন বেঞ্চমার্কের bootstrap পর্বে ইনজেকশন করা হয়:

@property --registered-no-inherit {
 
syntax: "<number>";
 
initial-value: 0;
 
inherits: false;
}

ফলাফল

16GB RAM সহ 2021 MacBook Pro (Apple M1 Pro) তে 20 পুনরাবৃত্তি সহ এই বেঞ্চমার্কগুলি চালানোর ফলে নিম্নলিখিত গড় পাওয়া যায়:

  • নিয়মিত সম্পত্তি যা উত্তরাধিকারসূত্রে পাওয়া যায় না: প্রতি সেকেন্ডে 290,269 রান (= 3.44µs প্রতি রান)
  • নিবন্ধিত কাস্টম সম্পত্তি যা উত্তরাধিকারসূত্রে পাওয়া যায় না: প্রতি সেকেন্ডে 214,110 রান (= 4.67µs প্রতি রান)

পরীক্ষাটি একাধিক রানের উপর পুনরাবৃত্তি হয়েছিল এবং এইগুলি ছিল সাধারণ ফলাফল।

উত্তরাধিকারী নয় এমন বৈশিষ্ট্যের ফলাফল সহ বার চার্ট। উচ্চ সংখ্যা দ্রুত সঞ্চালন.
চিত্র: উত্তরাধিকারী নয় এমন বৈশিষ্ট্যগুলির ফলাফল সহ বার চার্ট। উচ্চ সংখ্যা দ্রুত সঞ্চালন.

এখানে যে জিনিসটি দাঁড়িয়েছে তা হল যে বৈশিষ্ট্যগুলি উত্তরাধিকারসূত্রে পাওয়া যায় না সেগুলি উত্তরাধিকারসূত্রে পাওয়া বৈশিষ্ট্যগুলির তুলনায় অনেক দ্রুত কাজ করে৷ যদিও এটি নিয়মিত বৈশিষ্ট্যগুলির জন্য প্রত্যাশিত ছিল, এটি কাস্টম বৈশিষ্ট্যগুলির জন্যও সত্য।

  • নিয়মিত বৈশিষ্ট্যের জন্য রানের সংখ্যা 163 রান থেকে প্রতি সেকেন্ডে 290 হাজার রানের বেশি, পারফরম্যান্সে 1780% বৃদ্ধি!
  • কাস্টম বৈশিষ্ট্যের জন্য রানের সংখ্যা প্রতি সেকেন্ডে 252 রান থেকে সেকেন্ডে 214 হাজার রানের বেশি হয়েছে, পারফরম্যান্সে 848% বৃদ্ধি!

এর পিছনে মূল টেকঅওয়ে হল যে একটি কাস্টম সম্পত্তি নিবন্ধন করার সময় inherits: false ব্যবহার করা একটি অর্থপূর্ণ প্রভাব ফেলে। আপনি যদি inherits: false , আপনার অবশ্যই করা উচিত।

বোনাস বেঞ্চমার্ক: একাধিক কাস্টম সম্পত্তি নিবন্ধন

বেঞ্চমার্কের আরেকটি আকর্ষণীয় বিষয় হল প্রচুর কাস্টম সম্পত্তি নিবন্ধনের প্রভাব। এটি করার জন্য, --registered-no-inherit দিয়ে পরীক্ষাটি পুনরায় চালান এবং এটির সাথে 25,000টি অন্যান্য কাস্টম সম্পত্তি রেজিস্ট্রেশন আগে থেকেই করা হয়। এই কাস্টম বৈশিষ্ট্যগুলি :root এ ব্যবহার করা হয়।

এই রেজিস্ট্রেশনগুলি বেঞ্চমার্কের setup ধাপে করা হয়:

setup: () => {
 
const propertyRegistrations = [];
 
const declarations = [];

 
for (let i = 0; i < 25000; i++) {
    propertyRegistrations
.push(`@property --custom-${i} { syntax: "<number>"; initial-value: 0; inherits: true; }`);
    declarations
.push(`--custom-${i}: ${Math.random()}`);
 
}

  setCSS
(`${propertyRegistrations.join("\n")}
 
:root {
    $
{declarations.join("\n")}
 
}`);
},

এই বেঞ্চমার্কের জন্য প্রতি সেকেন্ডে রান "নিবন্ধিত কাস্টম প্রপার্টি যা উত্তরাধিকারসূত্রে পাওয়া যায় না" (প্রতি সেকেন্ডে 214,110 রান বনাম প্রতি সেকেন্ডে 213,158 রান) এর ফলাফলের সাথে খুব মিল, কিন্তু এটি দেখার মতো আকর্ষণীয় অংশ নয়। সর্বোপরি, এটি প্রত্যাশিত যে একটি কাস্টম সম্পত্তি পরিবর্তন করা অন্যান্য বৈশিষ্ট্য থেকে নিবন্ধন দ্বারা প্রভাবিত হয় না।

এই পরীক্ষার আকর্ষণীয় অংশ হল নিবন্ধনগুলির প্রভাব পরিমাপ করা। DevTools-এ ঘুরে, আপনি দেখতে পাচ্ছেন যে 25,000 কাস্টম সম্পত্তি নিবন্ধনের প্রাথমিক শৈলী পুনঃগণনা খরচ 30ms এর একটু বেশি। একবার এটি হয়ে গেলে, এই নিবন্ধনগুলির উপস্থিতি জিনিসগুলিতে আর কোনও প্রভাব ফেলবে না।

25k কাস্টম সম্পত্তি নিবন্ধন করার জন্য 'পুনরায় গণনা করা শৈলী' খরচ সহ DevTools স্ক্রিনশট হাইলাইট করা হয়েছে। টুলটিপ ইঙ্গিত করে যে এটি 32.42ms সময় নিয়েছে
চিত্র: 25k কাস্টম সম্পত্তি নিবন্ধন করার জন্য "পুনরায় গণনা করা শৈলী" খরচ সহ DevTools স্ক্রিনশট হাইলাইট করা হয়েছে। টুলটিপ ইঙ্গিত করে যে এটি 32.42ms সময় নিয়েছে

উপসংহার এবং গ্রহণ

সংক্ষেপে এই সমস্ত থেকে তিনটি টেকওয়ে রয়েছে:

  • @property সাথে একটি কাস্টম প্রপার্টি রেজিস্টার করা সামান্য পারফরম্যান্স খরচে আসে। এই খরচ প্রায়ই নগণ্য কারণ কাস্টম বৈশিষ্ট্য নিবন্ধন করে আপনি তাদের সম্পূর্ণ সম্ভাবনা আনলক করেন যা তা না করে অর্জন করা সম্ভব নয়।

  • inherits: false একটি অর্থপূর্ণ প্রভাব আছে। এটি দিয়ে আপনি সম্পত্তিকে উত্তরাধিকারী হতে বাধা দেন। যখন সম্পত্তির মান পরিবর্তিত হয় তাই সমগ্র সাবট্রির পরিবর্তে শুধুমাত্র মিলে যাওয়া উপাদানের শৈলীকে প্রভাবিত করে।

  • @property রেজিস্ট্রেশনের বিপরীতে অল্প কিছু থাকার ফলে স্টাইল পুনঃগণনাকে প্রভাবিত করে না। নিবন্ধন করার সময় শুধুমাত্র একটি খুব ছোট অগ্রিম খরচ আছে কিন্তু একবার এটি সম্পন্ন হলে আপনি ভাল।