Theppitak's blog

My personal blog.

14 สิงหาคม 2558

Patani Malay Support in Fonts-TLWG with Teochew Experiment

โครงการ fonts-tlwg กำลังอยู่ระหว่างพัฒนาเพิ่มเติมให้รองรับภาษาชาติพันธุ์ที่เขียนด้วยอักษรไทยได้สมบูรณ์ยิ่งขึ้น โดยรอบนี้เจาะไปที่ภาษามลายูปาตานีที่นอกจากจะต้องผ่อนคลายกฎการซ้อนอักขระต่าง ๆ แล้ว ยังต้องการอักขระเพิ่มเติมจากบล็อคยูนิโค้ดภาษาไทยอีก 4 ตัว เพื่อเป็นเครื่องหมายกำกับเสียงอ่าน กล่าวคือ:

  • U+0303 COMBINING TILDE คือสัญลักษณ์ตัวหนอนที่เขียนกำกับเหนือพยัญชนะเพื่อให้ออกเสียงขึ้นจมูก (เสียงนาสิก) คงได้แนวคิดจากตัว ñ ในภาษาสเปน
  • U+0331 COMBINING MACRON BELOW คือสัญลักษณ์ขีดเส้นใต้พยัญชนะเพื่อตัดเสียงนาสิกออกจากพยัญชนะนาสิกของไทย 4 ตัว คือ ง ญ น ม
  • U+02BC MODIFIER LETTER APOSTROPHE ใช้ในการลดรูปคำให้เหลือจำนวนพยางค์น้อยลง
  • U+02D7 MODIFIER LETTER MINUS ใช้ในการกล้ำสองพยางค์ให้ต่อเนื่องเป็นพยางค์เดียว

สองตัวหลังดูไม่มีอะไรมาก แค่เพิ่ม glyph ให้ก็ดูจะเพียงพอ แต่สองคัวแรกซึ่งเป็นอักขระประกอบ (combining character) ก็เลยมีปฏิสัมพันธ์กับสระบน-ล่าง วรรณยุกต์ และเครื่องหมายกำกับเสียงของไทย ทำให้ต้องมาจัดระเบียบใหม่ ซึ่งปรากฏว่าต้องทั้งขยับอักษรไทย ทั้งเปลี่ยนลำดับ normalization ของยูนิโค้ดด้วย

ตัวหนอนบอกเสียงนาสิกดูจะไม่มีประเด็นอะไรสำหรับภาษามลายูปาตานี เนื่องจากคำที่มีเครื่องหมายนี้จะไม่มีสระบนและวรรณยุกต์กำกับอยู่แล้ว ส่วนตัวขีดเส้นใต้ก็ควรขีดเส้นใต้พยัญชนะที่ความกว้างไม่เท่ากันให้สวยงามทั้งสี่ตัวด้วย (กล่าวคือ ไม่ใช่ใช้อักขระเดียวขีดเส้นใต้อักขระทุกตัว แต่ความยาวของขีดควรปรับตามความกว้างของพยัญชนะด้วย) รวมทั้งต้องตัดเชิง ญ เมื่อขีดเส้นใต้ด้วย

ก็ดูไม่ยุ่งยากอะไร ตัวหนอนก็แค่ทำประหนึ่งมันเป็นสระอิอีอึอือเสียก็จบ ส่วนตัวขีดเส้นใต้ก็สร้าง ligature glyph สำหรับ ง ญ น ม ขีดเส้นใต้สำเร็จรูปเท่านั้นเอง ผลออกมาก็งามน่าแล

Patani Malay text using Garuda

แต่ช้าก่อน ฟีเจอร์ของภาษามลายูปาตานีฟังดูน่าสนใจไม่น้อยสำหรับคนพูดภาษาแต้จิ๋วอย่างผม

  • เสียงนาสิกเป็นตัวแยกความหมายของคำในภาษาแต้จิ๋ว ภาษาไทยมีเสียงนาสิกแค่ไม่กี่เสียง คือ ง ญ ณ น ม ถ้าพยัญชนะต้นไม่ใช่เสียงพวกนี้ก็จะไม่ออกเสียงขึ้นจมูก (ญ ไม่มีเสียงนาสิกในไทยกลาง แต่ยังมีในไทยอีสาน [ดู blog เก่าประกอบ] และอาจจะมี ห ฮ ที่ยังขึ้นจมูกบ้างไม่ขึ้นบ้างแล้วแต่สำเนียง) แต่ภาษาแต้จิ๋วสามารถขึ้นจมูกได้ทุกพยางค์ และยังเป็นตัวแยกความหมายอีกด้วย เช่น คำว่า 碗 ที่แปลว่า ชาม จะออกเสียงเป็น อั้ว พร้อมเสียงขึ้นจมูก ซึ่งจะแตกต่างจากคำว่า 我 ที่เป็นสรรพนามบุรุษที่หนึ่งเอกพจน์ ซึ่งออกเสียงเป็น อั้ว เหมือนกันแต่ไม่ขึ้นจมูก ในเมื่อเสียงนาสิกมีความสำคัญถึงขนาดแยกความหมายของคำในภาษาแต้จิ๋วเช่นนี้ ตัวหนอนกำกับเสียงนาสิกของมลายูปาตานีจึงน่าจะนำมาใช้กับแต้จิ๋วได้
  • พยัญชนะที่ตัดเสียงนาสิกในภาษาแต้จิ๋วก็มี เช่นในคำว่า 牛 ที่แปลว่า วัว จะออกเสียงเป็น ก๎งู๊ คือเป็น ง ที่กล้ำกับเสียง ก ที่คอและไม่ขึ้นจมูก เสียงพยัญชนะนี้ไม่มีในภาษาไทย และมีความยากลำบากในการเขียนด้วยอักษรไทยมาก ตัวขีดเส้นใต้ตัดเสียงนาสิกของมลายูปาตานีก็น่าจะนำมาใช้ได้เช่นกัน

แต่การนำเครื่องหมายทั้งสองมาใช้กับภาษาแต้จิ๋วก็ทำให้พบกับ requirement ที่สูงขึ้น เพราะพยางค์ในภาษาแต้จิ๋วสามารถผสมกับสระและวรรณยุกต์ของไทยได้อย่างอิสระกว่ามาก ทำให้ตัวหนอนบอกเสียงนาสิกต้องสามารถผสมกับไม้หันอากาศและสระอิอีอึอือและใช้วรรณยุกต์ได้ด้วย ส่วนตัวขีดเส้นใต้ก็ต้องผสมกับสระล่างได้ด้วยเช่นกัน

ในการทำฟอนต์นั้น ไม่ใช่ว่าเราจะรองรับเฉพาะอักขรวิธีที่กำหนดไว้เท่านั้น แต่ยังควรเตรียม fallback สำหรับรองรับการแสดงผลรูปแบบที่อยู่นอกเหนือจากที่กำหนดด้วยตามสมควร ถึงแม้ไม่มีโจทย์ภาษาแต้จิ๋ว ก็ต้องคิดเผื่ออยู่แล้วว่าจะจัดการกับกรณีทั่วไปที่อยู่นอกเหนือข้อกำหนดของภาษามลายูปาตานีอย่างไร การมีโจทย์สมมุติของภาษาแต้จิ๋วเช่นนี้ก็เท่ากับทำให้เห็นแนวทางชัดเจนยิ่งขึ้น

ประเด็นของโจทย์ก็คือ:

  • ตัวหนอนนาสิก ฐานข้อมูลอักขระยูนิโค้ดกำหนดให้มี combining class 220 ในขณะที่วรรณยุกต์ไทยเป็น class 107 ตามหลัก normalization ของยูนิโค้ด จะเรียงลำดับอักขระที่อยู่ระหว่างอักขระ class 0 (ได้แก่พยัญชนะบนบรรทัด) ตามลำดับ combining class จากน้อยไปหามาก (เรียกว่า canonical order) ผลคือ เมื่อตัวหนอนนาสิกผสมกับวรรณยุกต์ไทย rendering engine จะสลับมันไปอยู่หลังวรรณยุกต์ก่อนส่งให้ฟอนต์แสดงผลเสมอ แต่สิ่งที่เราต้องการคือวางตัวหนอนก่อนวรรณยุกต์ เพื่อให้อยู่ชิดพยัญชนะ แล้วจึงซ้อนวรรณยุกต์ทับทีหลัง ดังนั้น ฟอนต์จะต้องรู้จักกลับลำดับคืนด้วย แต่ถ้ามีสระบนมาคั่นกลางก็ไม่ต้องทำอะไร เพราะสระบน, ไม้ไต่คู้, ทัณฑฆาต, นิคหิต, ยามักการ ต่างถูกกำหนด combining class เป็น 0 เหมือนพยัญชนะบนเส้นบรรทัด จึงเป็นจุดกั้นแบ่งโซนการเรียงลำดับ combining character โดยปริยาย
  • ตัวขีดเส้นใต้ ฐานข้อมูลอักขระยูนิโค้ดกำหนดให้มี combining class 220 ในขณะที่สระอุ สระอู มี combining class เป็น 103 (แต่ Uniscribe และ Harfbuzz แก้เองให้เป็น 3 ด้วยเหตุผลใดไม่อาจทราบได้) และพินทุมี combining class เป็น 9 ทำให้ตัวขีดเส้นใต้จะถูก rendering engine normalize ให้ไปอยู่หลังสระล่างและพินทุเสมอ และถ้ามีวรรณยุกต์ก็จะเลื่อนไปอยู่หลังวรรณยุกต์ด้วย แต่สิ่งที่เราต้องการคือวางตัวขีดเส้นใต้ก่อนเพื่อน เพื่อให้อยู่ชิดพยัญชนะ ดังนั้น ฟอนต์จะต้องรู้จักกลับลำดับคืนด้วย
  • พินทุกับสระล่าง ตาม combining class ในฐานข้อมูลอักขระยูนิโค้ดแล้ว พินทุ (class 9) จะต้องมาก่อนสระล่าง (class 103) ใน canonical order ซึ่งเป็นสิ่งที่ต้องการอยู่แล้วสำหรับภาษามลายูปาตานี แต่ด้วยเหตุผลกลใดไม่ทราบได้ Uniscribe ของไมโครซอฟท์ได้แก้ทับคลาสของสระล่างจาก 103 เป็น 3 ทำให้สระล่างมาก่อนพินทุ และเพื่อความเข้ากันได้กับ Uniscribe ทำให้ Harfbuzz ก็ทำอย่างเดียวกันด้วย สิ่งที่ฟอนต์ต้องทำคือกลับลำดับนี้คืนอีกชั้นหนึ่ง

ปัญหาของตัวหนอนนาสิกไม่มีอะไรซับซ้อน แก้ได้ด้วยการเพิ่มกฎ GSUB สำหรับสลับตัวหนอนนาสิกกับวรรณยุกต์ทั้งสี่ตัวเท่านั้นก็จบ และอาจจะนับตัวหนอนเป็นสระบนอีกหนึ่งตัวในกฎที่ยกไม้ไต่คู้ นิคหิต ยามักการขึ้นสูงและย่อขนาดลงเพื่อความสวยงามสักหน่อยด้วย

แต่ปัญหาของตัวขีดเส้นใต้ซับซ้อนกว่าที่คาดไว้มาก เพราะมีประเด็นอื่นที่ต้องพิจารณาร่วมด้วยอีก 2 ประเด็น คือ

  • การขีดเส้นใต้ ง ญ น ม ซึ่งความกว้างไม่เท่ากัน จัดการได้ด้วยการสร้าง precomposed glyph ที่ขีดเส้นใต้พยัญชนะดังกล่าวเสร็จสรรพ (ตัดเชิงสำหรับ ญ ไปในตัว) พร้อมกฎ ligature สำหรับเปลี่ยนคู่อักขระให้เป็นตัว precomposed กฎนี้ต้องทำงานในขณะที่พยัญชนะดังกล่าวกับตัวขีดเส้นใต้อยู่ติดกัน
  • กฎเดิมที่จัดการกรณีคำว่า ปู่ ซึ่งจะเปลี่ยนวรรณยุกต์ให้เป็นตัวต่ำพร้อมกับสลับสระล่างกับวรรณยุกต์ให้วรรณยุกต์มาก่อน เพื่อจะได้วางหลบหาง ป ฝ ฟ (และอาจจะ ฬ) ด้วย GPOS ได้ กฎนี้ต้องทำงานในขณะที่สระล่างตามหลังด้วยวรรณยุกต์ทันที

การเพิ่มกฎสำหรับกลับลำดับตัวขีดเส้นใต้จึงต้องระวังที่จะไม่ไปกีดขวางการทำงานของกฎดังกล่าวด้วย โดยถ้าทำก่อน ตัวขีดเส้นใต้ก็จะมาขวางกฎ ปู่ ให้ไม่ทำงาน และถ้าทำทีหลัง วรรณยุกต์จากกฎ ปู่ ก็จะมาขวางกฎสร้าง precomposed glyph ให้ไม่ทำงาน

หลังจากคิดและทดลองอยู่หลายตลบ ผมก็ได้ข้อสรุปเป็นลำดับกฎว่าดังนี้ :-

สตริงเริ่มแรก: C + Macron + BV + T
Uniscribe/Harfbuzz: C + BV + T + Macron
1: สลับ T กับ Macron C + BV + Macron + T
2: สลับ BV กับ Macron C + Macron + BV + T
3: ligature C-Macron-lig + BV + T || C + Macron + BV + T
3: สลับ Macron กับ BV C + BV + Macron + T
4: สลับ Macron กับ T C + BV + T + Macron
general composition C + T.low + BV + Macron
5: สลับ BV กับ Macron C + T.low + Macron + BV

กล่าวคือ เริ่มจากเลื่อนตัวขีดเส้นใต้ไปชิดกับพยัญชนะก่อนเพื่อให้กฎ ligature ทำงาน และถ้าตัวขีดเส้นใต้ไม่ได้ทำให้เกิด ligature ก็เลื่อนกลับที่เดิมเพื่อให้กฎ ปู่ ใน general composition ทำงาน จากนั้นจึงสลับตัวขีดเส้นใต้ให้มาก่อนสระล่าง ก็เป็นอันได้ลำดับที่เราต้องการ

กรณีลำดับของพินทุกับสระล่างที่ Uniscribe/Harfbuzz แปลงสารมาก็ทำนองเดียวกัน มีความซับซ้อนเล็กน้อย แต่ก็แค่กับกฎ ปู่ เท่านั้น :-

สตริงเริ่มแรก: C + Ph + BV + T
Uniscribe/Harfbuzz: C + BV + Ph + T
1: สลับพินทุกับ BV C + Ph + BV + T
general composition: C + Ph + T.low + BV
2: สลับพินทุกับ T.low C + T.low + Ph + BV

กล่าวคือ สลับพินทุกับสระล่างกลับให้เป็นเหมือนเดิม แล้วปล่อยให้กฎ ปู่ ใน general composition ทำงาน เสร็จแล้วก็เลื่อนวรรณยุกต์ตัวต่ำให้เข้าไปชิดกับพยัญชนะ

ผลที่ได้ดูจะทำงานได้ดีเมื่อทำ stress test ด้วยภาษาแต้จิ๋ว:

Experimental Teochew text using Garuda

และเนื่องจาก requirement ในเรื่องตัวหนอนนาสิกและตัวขีดเส้นใต้ของภาษาแต้จิ๋ว เป็น superset ของมลายูปาตานี การผ่านการทดสอบด้วยภาษาแต้จิ๋วย่อมถือว่าครอบคลุมภาษามลายูปาตานีโดยปริยาย

สังเกตว่า ในขณะที่ทำภาษามลายูปาตานีในขั้นแรกนั้น ผมลืมกรณีที่พยัญชนะขีดเส้นใต้มีสระล่างไปเสียสนิทด้วยซ้ำ การเล่นกับภาษาแต้จิ๋วยังช่วยจับบั๊กนี้ให้ด้วย

เมื่อทดลองกับฟอนต์ Garuda เป็นที่พอใจแล้ว ก็มาสรุปขั้นตอนทุกอย่างอีกครั้งกับฟอนต์ Kinnari:

Experimental Teochew text using Kinnari

แล้วก็ใช้ฟอนต์ Kinnari เป็นแบบสำหรับฟอนต์อื่น ๆ ต่อไป โดยเริ่มจากฟอนต์ Norasi:

Experimental Teochew text using Norasi

ตอนนี้ทำถึงขั้นนี้เท่านั้นครับ ยังมีฟอนต์อื่น ๆ อีกสิบกว่าฟอนต์ในชุด fonts-tlwg ที่ผมจะค่อยทยอยทำต่อไปเมื่อมีเวลาว่าง คาดว่าคงใช้เวลาอีกหลายเดือน :-P

ผมสร้าง หน้าทดสอบ โดยใช้ web font ที่สร้างจากตัวล่าสุดที่อยู่ระหว่างพัฒนาไว้ด้วย คงจะอัปเดตไปเรื่อย ๆ ตามความคืบหน้าครับ

ในอีกด้านหนึ่ง ก็ได้ อภิปรายและเสนอแพตช์ สำหรับ Harfbuzz เพื่อขอตัดการ override combining class ของสระล่างของไทยเสียด้วย ดูจะได้รับความเห็นชอบ แต่ยังไม่ apply เสียที อาจจะเกรงความไม่เข้ากันกับ Uniscribe หรือเปล่าก็ไม่ทราบได้ แต่การแก้ในฟอนต์ที่ผมทำไปก็เท่ากับทำให้มันทำงานบนวินโดวส์ได้ด้วย

ป้ายกำกับ: ,

02 พฤษภาคม 2558

LibThai/LibDATrie Optimization Summary

นับจาก blog เรื่องการ micro-optimize libthai ซึ่งได้ทดลองใช้ LIKELY/UNLIKELY ในการลดการสะดุดของ pipeline ของ CPU ขณะทำ branching ซึ่งลดเวลาของการตัดคำของ libthai ลงได้เพียง 0.014% เท่านั้น แต่ปรากฏว่าเมื่อไปทำกับ libdatrie กลับให้ผลที่ดีกว่านั้น

โค้ดที่เป็นคอขวดจริง ๆ คือ da_get_base(), da_get_check() ซึ่งใช้อ่านช่องข้อมูล double array โดยมีการตรวจสอบขอบเขตของค่า index ของ array ด้วย เมื่อใส่ LIKELY() กำกับเงื่อนไขที่จะไม่ error แล้ว ปรากฏว่าเวลาที่ใช้ในการตัดคำลดลงอย่างมากเมื่อเทียบกับจุดอื่น ๆ โดยทั่วไป

แต่ก็อย่าคาดหวังอะไรมากกับ micro-optimization เพราะเวลาที่ลดลงได้จากการ micro-optimize libdatrie รวมแล้วคือ 1.94% แต่เมื่อเทียบกับ 0.014% ที่ได้ใน libthai ก็ถือว่าน่าตื่นตาตื่นใจไม่น้อย

อย่างไรก็ดี ในช่วงต่อมา ก็ได้มี คำแนะนำจากคุณ edgehogapp ว่าสามารถลดเวลาตัดคำของ libthai ลงได้ ถ้าแปลงรหัสข้อความจาก TIS-620 เป็น Unicode เพียงครั้งเดียวก่อนวิเคราะห์ แทนที่จะแปลงทุกครั้งที่วิเคราะห์แต่ละตัวอักษร ซึ่งเมื่อทดลองแล้วก็ปรากฏว่าสามารถลดเวลาลงได้อีกถึง 0.28%

ก่อนที่จะเตรียมออกรุ่น libdatrie และ libthai รุ่นใหม่ จึงมาวัดเวลาสรุปอีกครั้ง ว่าการออปติไมซ์ส่วนไหนให้ผลเท่าไร โดยใช้ test case เดียวกัน เวลาที่ callgrind วัดได้ในแต่ละกรณีคือ:

  • libthai เก่า + libdatrie เก่า: 39,000,827
  • libthai เก่า + libdatrie ใหม่: 38,242,294 (ลดลง 1.94%)
  • libthai ใหม่ + libdatrie เก่า: 38,851,449 (ลดลง 0.38%)
  • libthai ใหม่ + libdatrie ใหม่: 38,089,676 (ลดลง 2.34%)

เมื่อคิดเป็นอัตราเร็วที่เพิ่มขึ้น (1 / (1 - อัตราส่วนเวลาที่ลดลง) - 1):

  • libthai เก่า + libdatrie ใหม่: เร็วขึ้น 1.98%
  • libthai ใหม่ + libdatrie เก่า: เร็วขึ้น 0.38%
  • libthai ใหม่ + libdatrie ใหม่: เร็วขึ้น 2.39%

กล่าวคือ ความเร็วที่เพิ่มขึ้นส่วนใหญ่มาจากการออปติไมซ์ libdatrie และโดยรวมแล้วทำให้การตัดคำเร็วขึ้น 2.39%

ในการวัด ครั้งก่อน ผมได้แยกวัดเฉพาะเวลาที่ใช้ในการตัดคำจริง ไม่นับเวลาโหลดพจนานุกรม เพราะไม่ได้มีการเปลี่ยนแปลงใน libdatrie แต่ครั้งนี้ การออปติไมซ์ libdatrie จะมีผลทั้งสองส่วน ซึ่งเมื่อแยกวัดดูก็ได้ผลดังนี้:

เวลาที่ใช้ในการโหลดพจนานุกรม:

  • libthai ใหม่ + libdatrie เก่า: 663,463
  • libthai ใหม่ + libdatrie ใหม่: 633,318 (ลดลง 4.54%; เร็วขึ้น 4.76%)

เมื่อคิดเฉพาะเวลาที่ใช้ในการตัดคำ:

  • libthai ใหม่ + libdatrie เก่า: 38,851,449 - 663,463 = 38,187,986
  • libthai ใหม่ + libdatrie ใหม่: 38,089,676 - 633,318 = 37,456,358 (ลดลง 1.92%; เร็วขึ้น 1.95%)

กล่าวคือ การ micro-optimize libdatrie ส่งผลต่อการโหลดพจนานุกรมมากกว่าการตัดคำ และเฉพาะสำหรับการตัดคำ มีผลมากกว่าการ micro-optimize ตัว libthai เอง อย่างมาก (ลดเวลาลง 1.92% เมื่อเทียบกับ 0.014% จาก libthai)

นอกเหนือจากการ micro-optimize แล้ว libdatrie ยังมีรายการแก้บั๊กอีกรายการหนึ่ง ซึ่งคุณ Sergei Lebedev ได้รายงานเข้ามาทางอีเมลส่วนตัว โดยอ้างถึง บั๊กใน python wrapper ของ libdatrie เกี่ยวกับการ iterate trie ที่ว่างเปล่า

เตรียมออกรุ่นใหม่เร็ว ๆ นี้ละครับ ทั้ง libdatrie และ libthai

ป้ายกำกับ: ,

30 เมษายน 2558

Thanks

ขอขอบคุณย้อนหลัง สำหรับผู้สนับสนุนงานพัฒนาซอฟต์แวร์เสรีของผมในช่วงเดือนมกราคม 2558 ถึงเมษายน 2558 ที่ผ่านมาครับ คือ:

ขอให้ทุกท่านและครอบครัวมีความสุขความเจริญ สุขภาพแข็งแรงทั้งกายใจครับ

ช่วงสี่เดือนที่ผ่านมา หลังจากที่การใช้เวลากับครอบครัวเริ่มจะเข้าที่เข้าทาง ผมก็สามารถเจียดเวลามานั่งทำงานได้เป็นเวลามากขึ้น แม้จะไม่มากเท่าเดิม แต่ก็ทำให้งานคืบหน้าได้

นอกจากการ optimize libthai/libdatrie ที่เคยเขียนไปแล้ว ก็มีงานจิปาถะอื่น ๆ อีก เช่น

  • งานแปล Xfce [ต้องล็อกอิน] ทั้งไล่กวดและไล่หลัง Xfce 4.12 ที่ออกไป จนขณะนี้อัตราการแปลของภาษาไทยอยู่ที่ 95% แล้ว
  • งานแปล GNOME ซึ่งผมเข้าสู่สถานะ review only (ไม่แปลเองอย่าง active) แล้ว ก็ได้ตรวจทานคำแปลที่มีนักแปลส่งเข้ามา
  • งาน Debian ในช่วงที่ Jessie freeze อยู่ ผมก็ได้แต่เตรียมปรับแก้ประเด็นเล็ก ๆ น้อย ๆ ที่ระบบ QA ต่าง ๆ ของ Debian ตรวจพบเพื่อรออัปโหลดในรอบ Stretch (Jessie + 1) ต่อไป เช่น:
    • lintian ส่วนใหญ่เป็นปัญหารูปแบบแฟ้ม debian/copyright ที่เครื่องแจงอ่านได้ และแฟ้มที่ขาดข้อมูลลิขสิทธิ์ ซึ่งบางส่วนต้องแก้ที่ต้นน้ำ ผมก็ทำเตรียมไว้
    • reproducible ตรวจพบการใช้ timestamp ขณะ build ซึ่งทำให้แพกเกจต่าง ๆ build หลายครั้งแล้วได้ checksum ไม่เท่ากัน ซึ่งทั้งหมดต้องแก้ที่ต้นน้ำ
    • Debian font review ซึ่งแสดงตัวอย่างฟอนต์จากแพกเกจฟอนต์ต่าง ๆ ใน Debian พร้อมผลลัพธ์จากโปรแกรม fontlint ที่ตรวจหาปัญหาต่าง ๆ ในฟอนต์ ทำให้ต้องมา validate ฟอนต์ที่ต้นน้ำทีละตัว ทำให้ได้แก้ปัญหาต่าง ๆ ในเส้นโค้งตัวอักษรของฟอนต์ เช่น การซ้อนทับกันของเส้น, การขาดจุด extrema, การระบุพิกัดของจุดแบบไม่ใช่จำนวนเต็ม, การใช้ชื่อ glyph ที่ไม่ตรงตามมาตรฐาน, ความผิดปกติของตาราง GPOS ฯลฯ แล้วก็เลยเถิดไปถึงการปรับเส้นอักษรแบบยกเครื่องในบางฟอนต์ ซึ่งส่งผลให้ได้เส้นที่คมชัดขึ้นเมื่อแสดงบนจอภาพ
    ประเด็นอื่นที่นอกเหนือจากที่ QA ตรวจพบ เช่น ปรับให้แพกเกจบางตัวที่สามารถใช้เอกสารใน /usr/share/doc ร่วมกันได้ทำเป็น symlink ถึงกัน ตาม Policy 12.3 วรรค 5 เพื่อลดขนาดติดตั้ง
  • งานปรับปรุงฟอนต์ในชุด fonts-tlwg ตามที่ได้รับข้อเสนอแนะจากคุณ Martin Hosken ซึ่งจะนำไปสู่การปรับปรุงการรองรับภาษาชาติพันธุ์ต่อไป
  • การแก้บั๊กใน libdatrie ตามที่มีผู้รายงานเข้ามาทางเมลลิงลิสต์

ขณะนี้ Jessie ก็ได้ออกไปแล้ว รอบการพัฒนาใหม่ของ Stretch ก็กำลังเริ่ม ก็คงใกล้ได้เวลาปล่อยของที่ทำสะสมไว้

ป้ายกำกับ: , , , , ,

12 กุมภาพันธ์ 2558

Pretty Feb Calendar Solution

ใน blog ที่แล้ว ผมได้ตั้งคำถามเกี่ยวกับปฏิทินที่ลงตัวของเดือนกุมภาพันธ์ว่ามีปีใดบ้าง ปรากฏว่ามีผู้ร่วมสนุกมาใน คอมเมนต์ โดยคิดแยกเป็นกรณีต่าง ๆ ของการเลื่อนวันในสัปดาห์ของวันที่เดิมของแต่ละปี

ก่อนอื่น เงื่อนไขของปีปฏิทินสวยก็คือ วันที่ 1 กุมภาพันธ์ของปีนั้นจะตรงกับวันอาทิตย์ และปีนั้นต้องเป็นปีปกติสุรทิน คือเดือนกุมภาพันธ์มี 28 วัน

คุณ Hisoft Manager ได้ตรวจสอบการเลื่อนวันในสัปดาห์ของวันที่ 1 กุมภาพันธ์ของแต่ละปีที่ติดกัน และแยกอธิบายเป็นกรณี ๆ ความคิดเริ่มแรกผมก็คิดด้วยวิธีคล้าย ๆ กันนี้ โดยคิดเป็นเศษเหลือจากการหารด้วย 7 โดยในปีปกติสุรทินซึ่งมี 365 วัน จะทำให้ 1 กุมภาพันธ์ของปีถัดไปเลื่อนวันในสัปดาห์ไป 1 วัน (365 หารด้วย 7 เหลือเศษ 1) และในปีอธิกสุรทินซึ่งมี 366 วัน จะทำให้ 1 กุมภาพันธ์ของปีถัดไปเลื่อนวันในสัปดาห์ไป 2 วัน (366 หารด้วย 7 เหลือเศษ 2) จากนั้นก็ใช้เงื่อนไขนี้ตรวจสอบรูปแบบการเลื่อนที่ทำให้ 1 กุมภาพันธ์เลื่อนกลับมาตรงกับวันอาทิตย์อีกครั้ง โดยมีเงื่อนไขเพิ่มเติมว่าปีนั้นต้องไม่เป็นปีอธิกสุรทินด้วย

ผลที่ได้คือ ปีปฏิทินสวยจะเป็นลำดับที่สมาชิกเพิ่มค่าเป็นรูปแบบ 6-11-11 คือปี 2009, 2015, 2026, 2037, 2043, 2054, 2065, ... คิดเป็นสูตรทั่วไปได้ว่า เป็น ค.ศ. ที่หารด้วย 28 แล้วเหลือเศษ 10, 21 หรือ 27 แต่ลำดับนี้จะเปลี่ยนรูปแบบเมื่อผ่านจุดที่เป็นข้อยกเว้น คือเมื่อ ค.ศ. หารด้วย 100 ลงตัว แต่หารด้วย 400 ไม่ลงตัว คำตอบจึงต้องแบ่งเป็นช่วง ๆ เช่น ระหว่าง ค.ศ. 1801-1899 จะเป็นปีที่หารด้วย 28 แล้วเหลือเศษ 9, 15, 26 เป็นต้น และจะต้องพิจารณาตรงช่วงรอยต่อระหว่างช่วงเป็นจุด ๆ ไป

ผมจึงพยายามหาสูตรทั่วไปที่ครอบคลุมทุกช่วง โดยเริ่มตั้งแต่ ค.ศ. 1753 ซึ่งเป็นปีแรกที่ปฏิทินนับเป็นปกติหลังจากที่ เริ่มใช้ปฏิทิน Gregorian แทนปฏิทิน Julian ตามที่คำสั่ง cal ของ GNU ได้ implement โดยตัดวันที่ 3-13 กันยายน 1752 ออกจากปฏิทิน:

$ cal 9 1752
    กันยายน 1752
อา จ. อ. พ. พฤ ศ. ส.
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30



จุดเริ่มต้นของเราจึงเป็นวันที่ 1 กุมภาพันธ์ 1753 ซึ่งเป็นวันพฤหัสบดี:

$ cal 2 1753
  กุมภาพันธ์ 1753
อา จ. อ. พ. พฤ ศ. ส.
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28

ผมกำหนดให้เศษเหลือจากการหารด้วย 7 แทนวันต่าง ๆ ในสัปดาห์ โดย 0 แทนวันอาทิตย์, 1 แทนวันจันทร์, 2 แทนวันอังคาร, ... 6 แทนวันเสาร์ ดังนั้น เศษเหลือเริ่มต้นในปี 1753 ของเราจึงเป็น 4 คือวันพฤหัสบดี

สมมุติให้ y แทนปีที่ต้องการพิจารณา นับจากปี 1753 ถึงปี y ถ้าทุกปีมี 365 วัน วันในสัปดาห์จะเลื่อนไปข้างหน้าปีละ 1 วัน (365 หารด้วย 7 เหลือเศษ 1) นับได้ y - 1753 วัน หากนำมาบวกวันเริ่มต้นคือวันพฤหัสบดี (เศษ 4) แล้วหารด้วย 7 เพื่อดูเศษเหลือ ก็จะรู้วันในปฏิทินของปีนั้นว่าเป็นวันอะไร แต่นี่คือกรณีที่ทุกปีมี 365 วัน โดยยังไม่คิดปีอธิกสุรทิน:

วันในสัปดาห์ของ 1 ก.พ. ปี y = (4 + (y - 1753)) mod 7 ถ้าทุกปีเป็นปกติสุรทิน

ปีอธิกสุรทินที่เกิดแต่ละครั้งจะทำให้วันในปฏิทินเลื่อนเพิ่มอีก 1 วัน หากรู้จำนวนปีอธิกสุรทินตั้งแต่ปี 1753 ถึงปี (y - 1) ก็จะนำมาบวกเพิ่มเข้ากับการเลื่อนวันในสูตรข้างต้นนี้ก่อนหารด้วย 7 เอาเศษ

จำนวนปีอธิกสุรทินตั้งแต่ปี 1753 ถึงปี (y - 1) คำนวณได้จากจำนวนปีอธิกสุรทินตั้งแต่ปี 1 ถึงปี (y - 1) ลบด้วยจำนวนปีอธิกสุรทินตั้งแต่ปี 1 ถึงปี 1753

จำนวนปีอธิกสุรทินตั้งแต่ปี 1 ถึงปี (y - 1) = floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400)

จำนวนปีอธิกสุรทินตั้งแต่ปี 1 ถึงปี 1753 = floor(1753/4) - floor(1753/100) + floor(1753/400) = 438 - 17 + 4 = 425

จำนวนปีอธิกสุรทินตั้งแต่ปี 1753 ถึงปี (y - 1) = floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400) - 425

ดังนั้นจึงได้ว่า วันที่ 1 ก.พ. ปี y จะตรงกับวัน: (4 + (y - 1753) + floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400) - 425) mod 7

หักลบตัวเลขแล้วจะได้เป็น (y - 2174 + floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400)) mod 7

และในเมื่อ 2174 mod 7 = 4 จึงแทน 2174 ด้วย 4 ใน modulo ได้ ได้เป็น:

วันในสัปดาห์ของ 1 ก.พ. ปี y = (y - 4 + floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400)) mod 7

และได้ว่า ปีปฏิทินสวย คือปีที่เป็นปกติสุรทิน และวันในสัปดาห์ของ 1 ก.พ. เป็น 0 ซึ่งเขียนเป็นนิพจน์เงื่อนไขภาษา C ได้เป็น:

  ((y % 4 != 0) || (y % 100 == 0 && y % 400 != 0)) &&
  (y - 4 + floor((y-1)/4) - floor((y-1)/100) + floor((y-1)/400)) % 7 == 0

QED.

ป้ายกำกับ: ,

08 กุมภาพันธ์ 2558

Feb 2015 Calendar

เมื่อเดือนที่แล้ว ผมได้รับข้อความจากเพื่อน ซึ่งส่งต่อมาจากคนอื่นอีกที ความว่า:

ปีนี้เดือนกุมภาพันธ์ มี 4 อาทิตย์, 4 จันทร์, 4 อังคาร, 4 พุธ, 4 พฤหัส, 4 ศุกร์ และ 4 เสาร์ ซึ่งจะเกิดได้ทุก 823 ปี ชาวจีนถือเป็นปีถุงเงิน ถ้าส่งให้เพื่อนหรือ กลุ่มเพื่อน อย่างน้อย 5 คน จะมีเงินไหลเข้ามาใน 4 วัน ส่งภายใน 11 นาที หลังอ่านจบ

เพื่อนคงส่งมาขำ ๆ แต่ความ geek ในตัวผมไม่เข้าใครออกใคร เลยตอบเพื่อนไปว่า ความจริงแล้วมันเกิดได้ทุกปี ยกเว้นปีที่ ค.ศ. หารด้วย 400 ลงตัว หรือไม่ก็หารด้วย 4 ลงตัว แต่หารด้วย 100 ไม่ลงตัว ผมซีเรียสนะเฟ้ย! ;-P

ก็ปีไหนเดือนกุมภาพันธ์มี 28 วัน ก็ปีนั้นแหละ กุมภาพันธ์จะมี 4 อาทิตย์, 4 จันทร์ ฯลฯ 4 เสาร์ (4*7 = 28) ซึ่งมันก็แทบทุกปี

แต่อย่างไรก็ตาม ปฏิทินเดือนกุมภาพันธ์ปีนี้ก็ยังมีอะไรพิเศษที่น่าสนใจ:

$ cal 2 2015
  กุมภาพันธ์ 2015     
อา จ. อ. พ. พฤ ศ. ส.  
 1  2  3  4  5  6  7  
 8  9 10 11 12 13 14  
15 16 17 18 19 20 21  
22 23 24 25 26 27 28  


คือเรียงสี่สัปดาห์ลงตัวสวยงาม ไม่ขาดไม่เกิน จึงเกิดคำถามที่น่าสนใจว่า ปฏิทินอย่างนี้จะเกิดได้ในปีไหนบ้าง?

ตัวอย่างของปีที่ว่าก็เช่น:

$ cal 2 2009
  กุมภาพันธ์ 2009
อา จ. อ. พ. พฤ ศ. ส.
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28


$ cal 2 2026
  กุมภาพันธ์ 2026
อา จ. อ. พ. พฤ ศ. ส.
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28


$ cal 2 2037
  กุมภาพันธ์ 2037
อา จ. อ. พ. พฤ ศ. ส.
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28


$ cal 2 2043
  กุมภาพันธ์ 2043
อา จ. อ. พ. พฤ ศ. ส.
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28


แล้วจะมีปีไหนอีกนะ?

หมายเหตุ: ยังไงรอบก็ยังไม่ถึง 823 ปีอย่างที่เขาว่าอยู่ดี ก็ยังเดาไม่ออกว่าปีถุงเงิน เขาเอามาจากตำราไหน :-P

ข้อสังเกต:

  • ปฏิทินโหราศาสตร์จีนเป็นปฏิทินจันทรคติ
  • ข้อความเรื่องปีถุงเงินนี้ มีแพร่ในอินเทอร์เน็ตมาหลายรอบแล้ว

ขอบคุณเพื่อนที่ส่งข้อความนี้มา ทำให้ผมได้อะไรคิดเล่นสนุก ๆ ผมทิ้งไว้ให้ผู้อ่านลองคิดกันดูนะครับ

ป้ายกำกับ: ,

05 กุมภาพันธ์ 2558

Yet Another LibThai Optimization Effort

ความพยายามในการ optimize libthai ยังคงดำเนินต่อไป หลังจากที่ได้ ปรับอัลกอริทึม ไปแล้วในรุ่น 0.1.21 ที่ได้ลดเวลาการ recover จาก error ลง

ทำแคชให้กับกระบวนการ recover (ไม่สำเร็จ)

รอบนี้ ผมได้พยายามลดจำนวนการ recover ลง แต่ต้องล้มเลิกในที่สุด

แนวคิดคือ การ recover จากจุดใดจุดหนึ่งจะได้คำตอบเหมือนเดิมไม่ว่าจะคำนวณกี่ครั้ง และในเมื่ออัลกอริทึมที่ผมใช้มีการพิจารณากรณีต่าง ๆ หลายกรณี จึงมีโอกาสที่กรณีเหล่านั้นจะมาพบ error ที่จุดเดียวกัน การเก็บแคชของจุด recover ไว้จึงช่วยให้ไม่ต้องคำนวณซ้ำอีก

ในโค้ดเดิมของ libthai มีการทำแคชอย่างง่ายไว้ โดยเก็บผลการ recover ครั้งล่าสุดไว้ ซึ่งแม้ hit rate จะต่ำมาก แต่ก็กลับช่วยลดเวลาได้ ถ้าเอาโค้ดนี้ออกเวลาที่ใช้จะเพิ่มขึ้น แนวคิดที่จะทำครั้งนี้คือขยายแคชให้ครอบคลุมทั้งข้อความ ซึ่งจากการทดลอง ผลที่ได้คือสามารถลดจำนวนการ recover ได้มากขึ้นจริง แต่โสหุ้ยของการทำแคชก็เพิ่มขึ้นด้วย และด้วย hit rate ที่ยังต่ำอยู่ ทำให้เวลาที่ลดได้ไม่สามารถหักลบกับเวลาที่เพิ่มขึ้นจากการ access แคชทุกรอบได้ ไม่ว่าจะลดโสหุ้ยต่าง ๆ ของแคชลงเท่าใดก็ตาม สรุปคือผมต้องโยนแพตช์นี้ทิ้ง เพราะการทำแคชกลับทำให้ใช้เวลาเพิ่มขึ้น!

อย่างไรก็ดี สิ่งที่ได้คือการได้พิสูจน์ว่าวิธีนี้ใช้ไม่ได้ ไม่ต้องพยายามอีก ไปหาวิธีอื่นต่อไป แต่ก็ขอเขียนบันทึกเป็น blog เพื่อช่วยเตือนความจำตัวเองในอนาคต

บทเรียน:

  • จะทำแคช ให้ระวังโสหุ้ยของแคชด้วย
  • ถูกย้ำเตือนอีกครั้งว่า malloc() นั้นโสหุ้ยสูงมาก (แพตช์แรกผมใช้ malloc() จองแคช ปรากฏว่าเวลาพุ่งกระฉูด แพตช์ต่อมาจึงปรับเป็น fixed array แทน)
  • แนวคิดในการ optimize ไม่สามารถสรุปเอาเองด้วยหลักเหตุผลได้ ควรพิสูจน์ด้วย profiler เสมอ

Micro-optimization ด้วย LIKELY/UNLIKELY

แนวคิดถัดมาคือการใช้ ส่วนขยายของ GCC คือ __builtin_expect() ที่ใช้ช่วยคอมไพเลอร์สร้างโค้ดที่ลดการสะดุดของ pipeline ของ CPU เมื่อมี branching (ด้วยการจัดให้โค้ดของ branch ที่ทำงานบ่อยวางต่อจาก condition เพื่อให้ถูก fetch มารอในแคชไว้) โค้ดในโครงการต่าง ๆ ได้สร้างเป็นแพตเทิร์น LIKELY และ UNLIKELY ไว้ใช้ เช่น Linux kernel, GLib ของ GNOME, MFBT ของ Mozilla

ผมเห็นโค้ดเหล่านี้มาพอสมควร และคิดจะใช้ในโค้ดของตัวเองอยู่เหมือนกัน แต่ก่อนที่จะใช้ก็ควรทดลองเสียก่อน ว่ามันช่วยได้จริงไหม และได้มากน้อยแค่ไหน

มีคนเขียน blog เรื่อง How much do __builtin_expect(), likely(), and unlikely() improve performance? ไว้ พร้อมโค้ดทดสอบ ผมลองเลียนแบบตามเล่น ๆ เมื่อนานมาแล้ว ก็ไม่พบความแตกต่างอย่างมีนัยสำคัญ ทั้งนี้เพราะเครื่องมือที่ใช้วัด คือคำสั่ง time ไม่ได้ละเอียดพอ และเวลาที่ใช้รันโปรแกรมแต่ละครั้งก็ไม่เท่ากัน ขึ้นอยู่กับโหลดของระบบในขณะนั้นด้วย แต่รอบนี้ผมต้องการคำตอบจริงจังขึ้น จึงเปลี่ยนไปใช้ callgrind วัด ซึ่งได้ค่าละเอียดและเที่ยงตรงกว่า

โค้ดทดสอบ:

#include <stdio.h>

#ifndef NOLIKELY
#define LIKELY(expr) (__builtin_expect (!!(expr), 1))
#define UNLIKELY(expr) (__builtin_expect (!!(expr), 0))
#else
#define LIKELY(expr) (expr)
#define UNLIKELY(expr) (expr)
#endif

#define N_LOOP 10
#define N_DATA 8300000

static __attribute__ ((noinline)) int
count (int a)
{
  return ++a;
}

int main ()
{
  char p[N_DATA];
  int i, j;
  int c1, c2;

  for (i = 0; i < N_DATA; i++) {
    p[i] = (i % 10 == 0) ? 1 : 0;
  }

  c1 = c2 = 0;
  for (j = 0; j < N_LOOP; j++) {
    for (i = 0; i < N_DATA; i++) {
      if (LIKELY (p[i] == 0)) {
        c1 = count (c1);
      } else {
        c2 = count (c2);
      }
    }
  }

  printf ("Likely: %d, Unlikely: %d\n", c1, c2);

  return 0;
}

คอมไพล์:

$ cc -O2 likely.c -o likely
$ cc -O2 -DNOLIKELY likely.c -o nolikely

ทดสอบด้วยคำสั่ง time:

$ time ./nolikely
Likely: 74700000, Unlikely: 8300000

real 0m0.282s
user 0m0.272s
sys 0m0.008s
$ time ./likely
Likely: 74700000, Unlikely: 8300000

real 0m0.280s
user 0m0.272s
sys 0m0.004s

ไม่ได้แตกต่างกันมาก และวัดแต่ละเที่ยวก็ได้ค่าต่างกันจนถือว่าผลหยาบเกินไป ไม่สามารถเทียบกันได้

ทดสอบด้วย callgrind ซึ่งวัดแต่ละเที่ยวได้ค่าเดิมอย่างสม่ำเสมอ:

$ valgrind --tool=callgrind ./nolikely
==16287== Callgrind, a call-graph generating cache profiler
==16287== Copyright (C) 2002-2013, and GNU GPL'd, by Josef Weidendorfer et al.
==16287== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==16287== Command: ./nolikely
==16287== 
==16287== For interactive control, run 'callgrind_control -h'.
Likely: 74700000, Unlikely: 8300000
==16287== 
==16287== Events    : Ir
==16287== Collected : 938005876
==16287== 
==16287== I   refs:      938,005,876
$ valgrind --tool=callgrind ./likely
==16313== Callgrind, a call-graph generating cache profiler
==16313== Copyright (C) 2002-2013, and GNU GPL'd, by Josef Weidendorfer et al.
==16313== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==16313== Command: ./likely
==16313== 
==16313== For interactive control, run 'callgrind_control -h'.
Likely: 74700000, Unlikely: 8300000
==16313== 
==16313== Events    : Ir
==16313== Collected : 938005846
==16313== 
==16313== I   refs:      938,005,846

ผลที่ได้คือ callgrind วัดความแตกต่างของเวลาที่ใช้เมื่อใช้ LIKELY และ UNLIKELY ได้ แต่มีข้อสังเกตคือ

  • สามารถลดเวลาลงได้เพียงเล็กน้อยเท่านั้น (ซึ่งก็ควรเป็นเช่นนั้น เรารู้ว่านี่คือ micro-optimization)
  • ต้องคอมไพล์แบบออปติไมซ์ (-O2 ขึ้นไป ซึ่งเปิดใช้ตัวเลือก -freorder-blocks) ถ้าไม่ออปติไมซ์ ผลจะไม่แน่นอน บางกรณีได้โค้ดที่ทำงานเร็วขึ้น บางกรณีได้โค้ดที่ทำงานช้าลง
  • ถ้าใช้ LIKELY/UNLIKELY ในทางกลับกับที่ควรจะเป็น จะได้โค้ดที่ทำงานช้าลงเสมอ

นั่นคือสิ่งที่สังเกตได้ และเมื่อนำมาใช้กับ LibThai จริง ๆ ก็ได้พบกับความงุนงง เพราะจุดแรกที่ผมนำมาใช้ก็คือ การลดโสหุ้ยของแคชของการ recover ดังที่กล่าวไปข้างต้น โดยได้วัด hit rate คร่าว ๆ ซึ่งต่ำกว่า 2% และแน่ใจว่าการ hit cache นั้นควรใช้ UNLIKELY แน่ ๆ แต่ผลที่ได้คือ มันใช้เวลาเพิ่มขึ้น! (การใช้ LIKELY แทนก็ให้ผลไม่ต่างกัน)

ผมยังไม่สามารถเข้าใจได้ว่าทำไม มันอาจไปรบกวน dynamic branch prediction ของ CPU หรืออย่างไรก็ไม่ทราบได้

หลังจากโยนแพตช์เรื่องแคชของการ recover ทิ้งไปแล้ว ผมก็ถอยกลับมาลอง LIKELY และ UNLIKELY กับกรณีอื่นที่มีความแน่นอน 100% เช่น กับ lazy initialization ที่ทำงานเพียงครั้งเดียวเท่านั้น และกับ error handling ในกรณีที่ malloc() ไม่สำเร็จ ซึ่งปรากฏว่า มันลดเวลาได้จริง ๆ แฮะ

กล่าวคือ ลดเวลาที่ callgrind วัดได้ จาก 44,313,933 ลงเหลือ 44,307,666 คิดเป็น 0.014%

ถือว่าน้อยนิดสำหรับเป้าประสงค์ที่ต้องการ คือการ optimize LibThai แต่สิ่งที่ผมได้มากกว่านั้นคือการได้เรียนรู้ธรรมชาติของ LIKELY/UNLIKELY ว่าควรใช้กับเงื่อนไขที่แน่ใจ 100% เท่านั้น อย่าใช้พร่ำเพรื่อ!

และได้เข้าใจลึกซึ้งถึงสิ่งที่คู่มือ GCC เขียนไว้ว่า...

...as programmers are notoriously bad at predicting how their programs actually perform...

เข้าใจแล้วคร้าบ... <(_ _)>

หมายเหตุ: commit ไปแล้วครับ โครงการต่อไปคือ หาทาง optimize libdatrie

ป้ายกำกับ:

07 มกราคม 2558

Thanks

สวัสดีปีใหม่ครับ ชีวิตหลังแต่งงานของผมยังอยู่ระหว่างปรับจูนการใช้เวลาระหว่างครอบครัวกับงาน รวมถึงเคลียร์ภารกิจต่าง ๆ ที่เกี่ยวเนื่องกับงานแต่งงาน ทำให้งานคืบหน้าช้ากว่าที่เคย คาดว่าไม่นานคงจะเข้าที่เข้าทางครับ

ขอขอบคุณย้อนหลัง สำหรับผู้สนับสนุนงานพัฒนาซอฟต์แวร์เสรีของผมในช่วงเดือนตุลาคม 2557 ถึงต้นมกราคม 2558 ที่ผ่านมาครับ คือ:

  • เดือนตุลาคม 2557
    • ผู้ไม่แสดงตน 2 ท่าน
    • ผู้ไม่ประสงค์จะออกนาม 1 ท่าน
  • เดือนธันวาคม 2557
  • ต้นเดือนมกราคม 2558

ขอให้ทุกท่านการงานราบรื่น มีสุขภาพดีทั้งคนทั้งระบบคอมพิวเตอร์ที่ใช้นะครับ

และในโอกาสขึ้นปีใหม่นี้ ก็ขออวยพรให้ผู้อ่าน blog ของผมทุกท่านมีความสุข คิดสิ่งใดก็ขอให้สมความปรารถนาครับ

สำหรับงานในช่วงที่ผ่านมา ผมพยายามเจียดเวลาว่างเล็ก ๆ น้อย ๆ มาทำงานแปล โดยได้ตรวจทานคำแปลของ GNOME และเครื่องมือพื้นฐานของ Debian และปรับคำแปลของ Xfce ตามข้อความใหม่ที่เกิดขึ้น

และระหว่างการเดินทางไปทำภารกิจต่าง ๆ ก็ได้ปรับแผนที่ OpenStreetMap ในตัวเมืองขอนแก่นไปด้วย

งานหนึ่งที่ยังไม่ลืม คือ Iceweasel Challenge (Bug #425915) ที่ได้รับคำท้าของพี่โดมไว้ตั้งแต่เดือนกันยายนที่ผ่านมา แต่คอมพิวเตอร์ที่ผมใช้ทำงานเกิดมีปัญหาเปิดไม่ติด ว่าจะยกไปซ่อมก็ยังไม่มีเวลา เนื่องจากมีภารกิจทางบ้านอย่างต่อเนื่องในช่วงที่ผ่านมา คาดว่าสักพักคงซาลง และสามารถกลับมาทำต่อได้ครับ

ป้ายกำกับ: ,

hacker emblem
No PAD No UDD
No Violent Mob