گزشتہ 2 فروری 2017 کو، این ٹیلر نے CodeInWP میں ایک مضمون لکھا: 12 Things We Need to See From WordPress Page Builders in 2017 + Who Already Gets It Right۔ ہم ایک سال سے زائد عرصے سے Page Builder Sandwich کو مسلسل بہتر بنا رہے ہیں: ہم تحقیق کرتے ہیں، اپنے صارفین کی سنتے ہیں، اس لیے ہمارا خیال تھا کہ Page Builder Sandwich، CodeInWP کے “Who Got it Right” مضمون میں جگہ بنا لے گا۔ لیکن کارکردگی کی وجہ سے ایسا نہ ہو سکا۔
چنانچہ ہم نے اس بارے میں این سے پوچھا، اور انہوں نے جواب دیا:
…میں نے مفت ورژن کو تھوڑا استعمال کیا۔ یہ بہت امید افزا لگتا ہے: بہت سے ماڈیولز، کافی وجدانی UI، بہترین WYSIWYG تجربہ، ہر چیز آسانی سے ڈریگ ہو جاتی ہے۔ تاہم، جب صفحے پر تقریباً 10 ماڈیولز ہو گئے، تو ایڈیٹنگ کی سست رفتار کی وجہ سے اس پر کام جاری رکھنا مشکل ہو گیا…
چنانچہ ان کے مطابق، Page Builder Sandwich میں کارکردگی کے مسائل ہیں۔ اور چونکہ ہم ہمیشہ آراء سنتے ہیں، اس لیے ہم نے اس کا جائزہ لیا۔
کارکردگی کی جانچ پڑتال
ہمیں PBS کی کارکردگی چیک کرنے کے لیے ایک طریقے کی ضرورت تھی؛ چونکہ PBS زیادہ تر جاوا اسکرپٹ کے ذریعے براؤزر پر چلتا ہے، اس لیے Chrome کا Timeline profiler اس کام کے لیے بالکل درست ٹول تھا۔ ٹائم لائن پروفائلر یہ شناخت اور پیمائش کر سکتا ہے کہ رن ٹائم کے دوران ویب پیج میں کون سے پروسیسز چل رہے ہیں، جو رفتار کی رکاوٹوں کو تلاش کرنے کے لیے بہترین ہے۔
ہم نے سوچا کہ ہمیں پہلے سب سے آسان قدم کی جانچ کرنی چاہیے – یعنی اپنے ماؤس کو متعدد عناصر اور قطاروں پر گھمانا۔ آسان ہے نا؟ ہمیں پورا یقین تھا کہ اتنی آسان چیز سے ہمیں کارکردگی کے مسائل کا سامنا نہیں کرنا پڑے گا۔ بدقسمتی سے، ہم غلط ثابت ہوئے۔
جیسے ہی ہم نے ریکارڈ کا بٹن دبایا، ہم نے اسکرین پر تقریباً 2 سیکنڈ کے لیے ماؤس کو گھمایا، پھر اسٹاپ کا بٹن دبایا۔ پروفائلر سے حاصل ہونے والی ٹائم لائن یہ ہے:
اور ذرا دیکھیے، یہ کارکردگی کی رکاوٹوں سے بھرا پڑا ہے!
گراف میں بہت کچھ ہو رہا ہے، لیکن سب سے واضح مسئلہ یہ ہے کہ اس میں بہت سارے جامنی حصے ہیں (جو کہ نہیں ہونے چاہئیں)۔ مزید برآں، یہ جامنی حصے واضح طور پر کافی چوڑے ہیں، اور سبھی گراف کے عروج پر پہنچتے دکھائی دیتے ہیں۔ آپ یہ بھی دیکھیں گے کہ گراف ہمیشہ بھرا ہوا رہتا ہے، اور جامنی حصے تقریباً کبھی بھی صفر پر نہیں جاتے۔ ایسا لگتا ہے جیسے براؤزر ہمیشہ کچھ نہ کچھ کرنے میں مصروف رہتا ہے، اور تقریباً کبھی آرام نہیں کرتا۔
لیکن ان تمام جامنی حصوں کا اصل میں کیا مطلب ہے؟
پروفائلر نے مہربانی کرتے ہوئے ہمیں بتایا کہ انہیں reflows کہا جاتا ہے۔ Google Developer سائٹ:
Reflow دستاویز میں موجود عناصر کی پوزیشنوں اور جیومیٹریز کا دوبارہ حساب لگانے کے ویب براؤزر کے عمل کا نام ہے.. ری فلو براؤزر میں صارف کے لیے رکاوٹ بننے والا (user-blocking) عمل ہے۔
لہٰذا اس کا مطلب یہ ہے کہ PBS میں ماؤس گھمانے سے بے شمار ری فلوز متحرک ہوتے ہیں۔ اس سے بھی بدتر بات یہ ہے کہ ہر ری فلو براؤزر کو لمحہ بھر کے لیے روک دیتا ہے تاکہ وہ اپنے حساب کتاب مکمل کر سکے۔ زیادہ ری فلوز کا مطلب براؤزر کا بار بار رکنا ہے، اور تجربہ مزید اٹکنے والا (janky) ہو جاتا ہے۔ آسان الفاظ میں، بہت سے جامنی حصے = برا۔
تو معلوم ہوا کہ این درست کہہ رہی تھیں: ہمارے ہاں کارکردگی کا واقعی ایک بڑا مسئلہ موجود تھا، اس لیے ہماری ٹیم فوری طور پر اسے ٹھیک کرنے کے کام میں لگ گئی۔
اپنے Reflow / Jank کے مسئلے کو حل کرنا
کسی بھی کام سے پہلے، ہمیں یہ جاننے کے لیے کچھ تحقیق کرنے کی ضرورت تھی کہ آخر اس ری فلو کی اصل وجہ کیا ہے۔ ہمیں معلوم ہوا کہ بظاہر، جاوا اسکرپٹ میں بہت سی عام چیزیں براؤزر کو ری فلو کرنے پر مجبور کر سکتی ہیں۔
تقریباً کوئی بھی فنکشن جسے آپ DOM عنصر کی چوڑائی، اونچائی یا پوزیشن حاصل کرنے کے لیے استعمال کر سکتے ہیں، وہ ری فلو کا سبب بن سکتا ہے۔ پال آئرش نے جاوا اسکرپٹ میں موجود ان تمام پراپرٹیز اور فنکشنز کی ایک تفصیلی فہرست لکھی ہے جو ایسا کر سکتے ہیں۔ بری خبر یہ ہے کہ Page Builder Sandwich ان پراپرٹیز اور فنکشنز میں سے بہت سے استعمال کرتا ہے، جو سب ری فلو کا سبب بنتے ہیں:
- elem.getBoundingClientRect()
- window.getComputedStyle()
- window.scrollY
- اور بہت کچھ…
حل نمبر 1: بھاری ری فلو پراسیسز کو کیش کرنا
سب سے واضح حل یہ ہے کہ ان پراپرٹیز اور فنکشنز کو استعمال نہ کیا جائے۔ تاہم، PBS ان پر بہت زیادہ انحصار کرتا ہے اور ہم ان کے بغیر گزارا نہیں کر سکتے۔
ہمارے پاس اب یہی حل بچا تھا کہ ہم ان فنکشنز کی کالز کو جتنا ممکن ہو سکے کم کریں۔ مشکل بات یہ تھی کہ ہم نے PBS کے مختلف حصوں میں ان پراپرٹیز اور فنکشنز کو کثرت سے استعمال کیا تھا، اور اکثر اوقات، ہم نے ایک ہی DOM عنصر پر انہیں بار بار استعمال کیا تھا۔ اس مسئلے کو حل کرنے کے لیے، ہم نے ہر مسئلے والے پراپرٹی/فنکشن کے لیے اپنا کیشنگ طریقہ کار بنایا۔
مثال کے طور پر، getBoundingClientRect فنکشن ہمیں کسی DOM عنصر کی موجودہ top، left، width، height، right اور bottom کی پکسل پیمائش دیتا ہے۔ ہم PBS میں عنصر کی آؤٹ لائن کہاں بنانی ہے اس کا حساب لگانے کے لیے ہر وقت اس فنکشن کا استعمال کرتے ہیں۔ اس کے لیے جو کیشنگ فنکشن ہم نے بنایا وہ یہ ہے:
// Cache: Our client rects are kept here, we can use this to clear them later.
var elemsWithBoundingRects = [];
window.pbsGetBoundingClientRect = function( element ) {
// Check if we already got the client rect before.
if ( ! element._boundingClientRect ) {
// If not, get it then store it for future use.
element._boundingClientRect = element.getBoundingClientRect();
elemsWithBoundingRects.push( element );
}
return element._boundingClientRect;
};
getBoundingClientRect کو کال کرنے کے بجائے، اب ہم نے pbsGetBoundingClientRect کا استعمال کیا۔ اب جب بھی client rect طلب کیا جاتا ہے، ہم مستقبل کے حوالے کے لیے اس قدر کو محفوظ کر لیتے ہیں، جس سے ری فلو متحرک ہونے کی تعداد محدود ہو جاتی ہے۔
ہمیں اس وقت کیش کو صاف کرنے کے لیے بھی ایک طریقے کی ضرورت تھی جب محفوظ شدہ client rects باطل ہو جائیں۔ محفوظ کردہ client rect کی اقدار اس وقت تبدیل ہوتی ہیں جب صارف صفحہ اسکرول کرتا ہے یا اسکرین میں عناصر کو شامل/تبدیل/ختم کرتا ہے۔ لہٰذا ہم نے ایک کیش کلیئرنگ فنکشن بنایا جسے ہم جب بھی نئی client rect اقدار کی ضرورت ہو کال کر سکتے ہیں:
var clearClientRects = function() {
var i;
for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
if ( elemsWithBoundingRects[ i ] ) {
elemsWithBoundingRects[ i ]._boundingClientRect = null;
}
}
elemsWithBoundingRects = [];
};
ہم نے دیگر ایسے بھاری فنکشن کالز کے لیے بھی یہی کیشنگ طریقہ کار استعمال کیا جو ہم عام طور پر استعمال کرتے ہیں۔
حل نمبر 2: FastDom کا استعمال کرتے ہوئے Reads اور Writes کو یکجا کرنا
پہلے حل پر عمل درآمد سے PBS کی کارکردگی میں اضافہ ہوا، لیکن یہ کافی نہیں تھا۔
مزید گوگل کرنے پر، ہمیں ولسن پیج کا ‘layout thrashing’ کی روک تھام پر ایک مضمون ملا۔ ولسن کہتے ہیں:
لے آؤٹ تھریشنگ (ری فلو) اس وقت پیش آتی ہے جب جاوا اسکرپٹ بار بار DOM سے پڑھتا ہے اور اس میں لکھتا ہے، جس کے نتیجے میں دستاویز میں ری فلوز پیدا ہوتے ہیں۔
ظاہر ہے، صرف مخصوص ری فلو متحرک کرنے والی پراپرٹیز اور فنکشنز کے استعمال کو محدود کرنا ہی کافی نہیں ہے۔ جب آپ DOM پر پے در پے متعدد بار “reads” اور “writes” انجام دیتے ہیں، تو یہ بھی براؤزر ری فلو کا سبب بنتا ہے۔ ولسن کی سائٹ سے براہ راست ایک عام منظر نامہ پیش ہے:
// Read var h1 = element1.clientHeight; // Write (invalidates layout) element1.style.height = (h1 * 2) + 'px'; // Read (triggers reflow) var h2 = element2.clientHeight; // Write (invalidates layout) element2.style.height = (h2 * 2) + 'px'; // Read (triggers reflow) var h3 = element3.clientHeight; // Write (invalidates layout) element3.style.height = (h3 * 2) + 'px'; // Document reflows at end of frame
اوپر دی گئی مثال 3 ری فلوز کو متحرک کرتی ہے کیونکہ کسی بھی write کے بعد پورے صفحے کی پیمائشیں باطل ہو جاتی ہیں۔ اس کے لیے تجویز کردہ حل یہ ہے کہ کوڈ کو دوبارہ اس طرح ترتیب دیا جائے کہ تمام reads اور writes کو ایک ساتھ یکجا کر دیا جائے تاکہ یہ سارا عمل صرف 1 براؤزر ری فلو کا سبب بنے:
// Read var h1 = element1.clientHeight; var h2 = element2.clientHeight; var h3 = element3.clientHeight; // Write (invalidates layout) element1.style.height = (h1 * 2) + 'px'; element2.style.height = (h2 * 2) + 'px'; element3.style.height = (h3 * 2) + 'px'; // Document reflows at end of frame
اوپر دیا گیا اسنیپٹ مسئلے کو حل کرنا آسان ظاہر کرتا ہے۔ لیکن حقیقت میں، PBS میں اس کے لائف سائیکل کے دوران بہت سے مختلف مقامات پر اور بہت سے مختلف اوقات میں ری فلو پیدا کرنے والی چیزوں کی ریڈنگ اور رائٹنگ ہوتی ہے۔ جاوا اسکرپٹ کوڈ کی ہزاروں لائنوں کو دوبارہ ترتیب دینا ممکن نہیں ہے۔
خوش قسمتی سے، ولسن نے اپنے مضمون میں ایک حل بھی فراہم کیا۔ انہوں نے ان reads اور writes کو یکجا کرنے میں مدد کے لیے ایک چھوٹا یوٹیلیٹی اسکرپٹ تیار کیا، جسے FastDom کہا جاتا ہے۔
Page Builder Sandwich کا کچھ کوڈ پیش ہے جو FastDom کا استعمال کرتا ہے اور اس میں ہمارا اوپر دیا گیا حل نمبر 1 بھی شامل ہے:
// Bunch together the reads from other threads.
fastdom.measure( function() {
// Use our cached client rect.
var rect = window.pbsGetBoundingClientRect( elem );
// Bunch together the writes from other threads.
fastdom.mutate( function() {
elem.style.top = ( rect.top + window.pbsScrollY() ) + 'px';
elem.style.height = rect.height + 'px';
elem.style.left = rect.left + 'px';
elem.style.width = rect.width + 'px';
}.bind( this ) );
}.bind( this ) );
FastDom یہ کرتا ہے کہ یہ تمام fastdom.measure کالز اور تمام fastdom.mutate کالز کو یکجا کرتا ہے اور انہیں غیر ہم وقت ساز (asynchronously) طور پر چلاتا ہے، جس سے براؤزر میں ری فلوز کو کم کرنے کے لیے تمام reads اور writes ایک ساتھ گروپ ہو جاتے ہیں۔
حل نمبر 3: اسٹائل آبجیکٹس تک رسائی بھاری ہوتی ہے
ہمیں jsPerf کی مدد سے یہ بھی معلوم ہوا کہ اسٹائل حاصل کرنے والے بعض فنکشنز دوسروں کے مقابلے میں زیادہ تیز رفتار تھے۔
PBS میں، ہمیں عام طور پر کسی عنصر کے مخصوص اسٹائل کی موجودہ قدر حاصل کرنے کی ضرورت ہوتی ہے۔ مثال کے طور پر، ہم عام طور پر اوپر کا مارجن حاصل کرنے کے لیے اسے استعمال کرتے ہیں:
var styles = window.getComputedStyle( elem ); var marginTop = styles.marginTop;
لیکن، ہمیں معلوم ہوا کہ یہ طریقہ کار کافی زیادہ تیز رفتار ہے:
var marginTop = elem.style.marginTop;
تاہم، elem.style.marginTop اور getComputedStyle بہت مختلف ہیں۔ پہلا طریقہ صرف عنصر کا ان لائن اسٹائل دیتا ہے، اور کسی بھی اسٹائل شیٹ کو مدنظر نہیں رکھتا؛ جبکہ دوسرا طریقہ ان لائن اسٹائلز اور اسٹائل شیٹس دونوں کو مدنظر رکھتا ہے۔
مزید یہ کہ getComputedStyle کے نتیجے کو کیش کرنے کے باوجود بھی یہ واقعی سست تھا۔ جیسا کہ بعد میں معلوم ہوا، خود اسٹائل آبجیکٹ تک رسائی حاصل کرنا ہی ایک سست عمل ہے۔
خوش قسمتی سے، PBS عناصر کے ساتھ ان لائن اسٹائلز منسلک کرتا ہے، اور چونکہ ان لائن اسٹائلز کو (اکثر اوقات) اسٹائل شیٹس پر فوقیت حاصل ہوتی ہے، اس لیے ایسے مواقع ہوں گے جہاں ان لائن اسٹائل کی قدر حاصل کرنا ہی کافی ہوگا۔ چنانچہ اگر ہم اوپر دیے گئے دونوں طریقوں کو یکجا کر دیں، تو ہم زیادہ تیزی سے چلنے والا کوڈ تیار کر سکتے ہیں:
var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
var styles = window.getComputedStyle( elem );
marginTop = styles.marginTop;
}
کوڈ کی بہتریوں کے نتائج
ہمارے اوپر دیے گئے 3 حلوں کے لیے بہت زیادہ تحقیق، جانچ پڑتال اور عملی نفاذ کی ضرورت پڑی (اور اس کی وجہ سے ہمارے Fonts فیچر کے اجرا میں تاخیر ہوئی)، لیکن آخر کار یہ سب فائدہ مند ثابت ہوا۔
اسکرین پر 2 سیکنڈ کے لیے ماؤس گھمانے کے اپنے ابتدائی ٹیسٹ پر واپس آتے ہیں۔ یہ پرانی بدشکل/اٹکنے والی ٹائم لائن ہے:
اور کارکردگی میں بہتری کے بعد ہماری نئی ٹائم لائن ایسی دکھتی ہے:
جیسا کہ آپ دیکھ سکتے ہیں، گراف میں جامنی رنگ کی چیزیں کم ہیں۔ اور براؤزر کے ذریعے انجام دیے جانے والے پروسیسز کی مقدار میں نمایاں فرق ہے۔ نیچے مستطیل نما چیزیں بھی پتلی ہیں، جس کا مطلب ہے کہ پروسیسز پہلے کے مقابلے میں زیادہ تیزی سے چل رہے ہیں۔
اگر آپ پرانے اور نئے گراف میں اونچے/چوڑے جامنی حصوں کی مقدار کا موازنہ کریں، تو ہم کارکردگی میں زبردست 200% اضافے کو دیکھ رہے ہیں! ہم ہمہ جہت کارکردگی میں بھی اضافہ دیکھ رہے ہیں۔ بالکل بھی برا نہیں!
حاصل کلام اور اسباق
سچ بتائیں تو، این کے ذکر کرنے سے پہلے ہمارا خیال نہیں تھا کہ PBS میں کارکردگی کے مسائل ہیں۔ بنیادی طور پر اس لیے کہ ہم ڈویلپمنٹ کے لیے مخصوص مشینیں استعمال کر رہے تھے اور کارکردگی کی رکاوٹیں اتنی واضح نہیں تھیں۔ ماضی پر نظر ڈالیں تو، کارکردگی کی جانچ پڑتال اولین ترجیحات میں سے ایک ہونی چاہیے تھی۔ لیکن کم از کم اب، ورژن 4.3 کے بعد سے اس کا خیال رکھ لیا گیا ہے۔
ان لوگوں کے لیے جو اپنے ورڈپریس پلگ ان کی کارکردگی کو بہتر بنانا چاہتے ہیں، خاص طور پر اگر آپ بھی بھاری جاوا اسکرپٹ کا استعمال کر رہے ہیں، اس تجربے سے میرے اہم نکات یہ ہیں:
- ری فلو کو متحرک کرنے والی پراپرٹیز/فنکشنز کی کالز کو محدود/کیش کریں۔ رہنمائی کے لیے اسے استعمال کریں: What forces layout/reflow. The comprehensive list.
- DOM اسٹائلز میں “reads” اور “writes” کو یکجا کریں۔ یہ کام اپنے لیے کرنے کی خاطر FastDom کا استعمال کریں۔
- Chrome کے Timeline profiler کا استعمال کریں، اور یقینی بنائیں کہ آپ کے پاس کم سے کم ری فلوز (جامنی مواد) ہوں۔