Theppitak's blog

My personal blog.

23 ตุลาคม 2564

IBus-LibThai 0.1.5

IBus-LibThai 0.1.5 ออกไปแล้วเมื่อวันพุธที่ผ่านมา หลังจากไม่มี release มาเกือบ 5 ปี โดยมีเงื่อนไขหลักที่ทำให้จำเป็นต้องออกรุ่นใหม่คือปัญหาในระบบตั้งค่า อาการคือตั้งค่าเสร็จแล้วไม่มีผลต่อตัว engine ตั้งยังไงก็ได้ค่า default

ผมไม่ได้ติดตามการพัฒนาของ IBus มาระยะหนึ่ง จึงไม่ทราบว่ามีความเปลี่ยนแปลงอะไรบ้างจนทำให้วิธีตั้งค่าแบบเดิมใช้ไม่ได้ จึงเริ่มจากตรวจสอบ engine อื่นว่าปัจจุบันเขาทำยังไงกัน ก็ได้พบว่าหลาย engine ได้ย้ายจากการใช้ IBusConfig ซึ่งเป็น API ของ IBus เองสำหรับการจัดการค่าตั้งไปใช้ระบบอื่น บาง engine ถึงกับสร้างระบบ config ของตัวเองขึ้นมาเลย

แต่ผมก็ยังคงพยายามปรับแก้ภายใต้ API ของ IBus ต่อไป โดยเปลี่ยนจากการ restart IBus เพื่อ reload config มาเป็นการ watch ค่า config แล้วปรับใช้ค่าที่เปลี่ยนทันทีแทน ซึ่งก็ทำให้ engine เปลี่ยนพฤติกรรมตามค่าที่ผู้ใช้ตั้งได้ ภายใน session เดิม แต่ไม่สามารถ save ค่าไว้ให้มีผลใน session ถัดไปได้

ผมแน่ใจว่าทำทุกอย่างเท่าที่ document บอกแล้ว ถ้าจะมีอะไรที่ undocumented ก็ต้องแกะซอร์สของ IBus ซึ่งพอลงมือแกะก็พบว่า แม้แต่ IBus แกนหลักเองก็ไม่ใช้ IBusConfig แต่ไปใช้ GSettings ของ GLib!

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

ด้วยระบบ GSettings ซึ่งใช้ dconf เป็น backend ค่าตั้งต่างๆ จะสามารถเข้าถึงได้ด้วยโปรแกรม dconf-editor

ค่า config ของ IBus-LibThai ใน dconf editor

โดยค่า config แต่ละค่าจะมีคำอธิบายจาก GSchema ที่เตรียมไว้

คำบรรยายคีย์ kb-layout ของ IBus-LibThai ใน dconf editor

และผู้ใช้ IBus ก็ยังคงสามารถปรับแต่ง IBus-LibThai ได้จาก preferences ของ engine ใน IBus Preferences ตามปกติ

Preferences ของ IBus-LibThai

พร้อมกันนี้ ยังมีความเปลี่ยนแปลงอื่นในส่วนของผังแป้นพิมพ์:

  • มีการปรับโครงสร้างของข้อมูลผังแป้นพิมพ์เพื่อให้เข้าใจง่าย และสะดวกต่อการปรับแก้
  • ปรับแก้ผังปัตตะโชติที่ยังคลาดเคลื่อนจากแพลตฟอร์มอื่น โดย ขอหารือ กับคุณ @sirn ซึ่งเป็นผู้ใช้ผังปัตตะโชติ ซึ่งคุณ @sirn ก็ได้เสนอแนะเพิ่มเติม จนกระทั่งได้ตำแหน่งปุ่มสำหรับอักษรไทยเพิ่ม คือ ฃ ขวด, ฅ คน, ลากข้างยาว, ยามักการ
  • เพิ่มผัง มนูญชัย ซึ่งเป็นผังแป้นพิมพ์ใหม่ล่าสุดที่เกิดจากการออปติไมซ์ด้วย AI จากตัวอย่างเอกสารร่วมสมัย ผู้ใช้ลินุกซ์ที่สนใจทดสอบหรือใช้งานผังใหม่นี้จึงสามารถใช้งานได้ผ่าน IBus-LibThai

ป้ายกำกับ: ,

23 สิงหาคม 2561

LibThai 0.1.28 and its Consequences

บันทึกการเปลี่ยนแปลงต่าง ๆ ที่มาใน LibThai 0.1.28 และผลพวงทั้งหลายหลังจากนั้น

LibThai 0.1.28 ออกไปตั้งแต่ต้นเดือน โดยรุ่นนี้มีรายการเปลี่ยนแปลงสำคัญ ๆ คือ:

  • แก้ปัญหาขาด header <thai/thwchar.h> ใน header ที่เกี่ยวกับฟังก์ชัน wide char หลาย ๆ ตัว เช่น thwbrk.h, thwcoll.h ฯลฯ ซึ่งเป็นปัญหาที่พบระหว่างเขียนโปรแกรมตัวหนึ่งที่เรียกใช้ libthai ทำให้ต้อง include thai/thwchar.h เอง ซึ่งไม่สะดวก ในรุ่นนี้สามารถ include header ที่ต้องการแล้วเรียกใช้งานได้เลย ไม่ต้องเพิ่ม include เองอีกแล้ว
  • ปรับโค้ดให้เป็นไปตาม C90 (ANSI C) มากขึ้น เป็นผลพวงจากที่ได้ทำกับ libdatrie 0.2.12 มาแล้ว
  • ปรับข้อมูลพจนานุกรมตัดคำ โดยในรุ่นนี้ได้รับความช่วยเหลือจากคุณ @nuttee15 จาก metamedia technology ที่ได้เสนอคำเพิ่มเข้ามาใน Issue #2 ที่เปิดไว้รับเสนอคำใหม่ในพจนานุกรมตัดคำ

จากนั้นก็ได้ upload debian package พร้อมความเปลี่ยนแปลงอย่างอื่น คือ เพิ่ม pkg-config ให้เป็น dependency ของ libthai-dev เพื่อให้แน่ใจว่า libthai.pc จะสามารถทำงานได้แม้ในระบบที่ติดตั้งแบบเล็กที่สุด (เป็นปัญหาที่พบระหว่างที่ทำงานชิ้นหนึ่งร่วมกับ metamedia technology) และ การรองรับการ build ที่ไม่ต้องใช้ (fake)root

รายการคำจากพจนานุกรมตัดคำของ libthai ก็ได้นำไปใช้ สร้าง hyphenation pattern ที่โครงการ thailatex ซึ่งขณะนี้กลายสภาพเป็นเพียงที่พักงานพัฒนา hyphenation pattern เท่านั้น จากนั้นจึงได้เสนอ pull request สำหรับ update hyphenation pattern สำหรับภาษาไทยในโครงการ TeX hyphenation patterns ซึ่งต้องรอการ merge เพื่อให้มีผลที่ต้นน้ำต่อไป

จาก TeX hyphenation pattern ก็ต้อง sync มายังเครื่องมือตัดคำสำหรับเอกสาร LaTeX ด้วย คือ swath ซึ่งนอกจากการปรับพจนานุกรมแล้ว ก็ได้ปรับโค้ดเล็ก ๆ น้อย ๆ ตามที่เคยทำในทุกรุ่นที่ผ่านมา โดยสิ่งที่ทำในรุ่นนี้คือ:

  • ทดลอง build โดยใช้ CFLAGS -Wall แล้วแก้ warning ต่าง ๆ
  • จากการแก้ warning ที่พบในโค้ดส่วน RTF filter ทำให้ตรวจพบความผิดปกติใน method หนึ่งที่ตัวฟังก์ชันทำงานตรงข้ามกับชื่อ คือ RTFToken::isEmpty()

    โค้ดส่วนจัดการ RTF นี้ ไม่ค่อยมีใครเรียกใช้ ความจริงผมเคยเสนอจะตัดทิ้งไปแล้ว แต่ด้วยความช่วยเหลือของคนในชุมชน (คุณวิทยา ไตรสารวัฒนะ) ทำให้ได้วิธีทดสอบความถูกต้องของโปรแกรม จึงยังคงเก็บไว้ แต่เนื่องจากเวลานั้น swath ยังไม่เริ่มทำ TDD จึงยังไม่ได้ใส่ test case ไว้ใน source tree

    เพื่อจะตรวจแก้ฟังก์ชันที่สงสัยนี้ ผมจึงต้องไปดึงเอกสารตัวอย่างมาจาก thread เก่าที่ว่า แล้วนำมา เพิ่ม test case เสียก่อน หากคุณสงสัยว่าทำไม source tarball ของ swath รุ่นนี้ถึงโตขึ้นจนผิดสังเกต มันก็มาจากเอกสาร RTF ทดสอบนี้นี่เอง

    จากนั้น จึงได้ แก้ฟังก์ชันที่สงสัย นั้น แล้วรัน test เปรียบเทียบ output ดูโดยใช้ LibreOffice ปรากฏว่าเป็นการแก้ที่ถูกต้องแล้ว เพราะมันทำให้ได้จุดตัดคำครบถ้วนสมบูรณ์ขึ้น

    ไม่ว่าจะมีใครใช้หรือไม่ก็ตาม แต่คอมไพเลอร์ยังคงคอมไพล์มันอยู่ และนำผมเข้ามาเจอและแก้บั๊กจนได้

  • แก้ warning อื่น ๆ และทำความสะอาดโค้ดเล็ก ๆ น้อย ๆ
  • สุดท้าย มี pull request ของคุณ @pepa65 ที่เสนอไว้นานแล้ว เพื่อร่างแฟ้ม INSTALL ที่อธิบายความแตกต่างของวิธี build swath จาก git กับจาก released tarball ซึ่งผมก็เห็นว่ามีประโยชน์กว่าแฟ้ม INSTALL ที่ GNU automake มันเติมให้แบบอัตโนมัติ จึง merge เข้ามาเสีย

แล้วก็ออกรุ่น swath 0.6.1 ตามมาด้วย Debian upload ซึ่งก่อนอัปโหลดก็ได้ปรับ branch layout ของ swath packaging ตาม DEP-14 ด้วย

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

25 ตุลาคม 2560

LibThai 0.1.27 & Word Suggestion Channel

LibThai 0.1.27 ออกแล้ว โดยห่างจาก รุ่นที่แล้ว ถึง 10 เดือน ก็สมควรแก่เวลาปล่อยข้อมูลพจนานุกรมที่แก้ไขสะสมไว้ในช่วงเวลาดังกล่าว รวมถึงแก้ปัญหา reproducibility ที่ถูกจับโดยโครงการ Reproducible Builds ของ Debian

พูดถึงประเด็นหลังก่อน ปัญหา reproducibility ที่ตรวจพบก็คือ มีการ capture build path ลงใน document ที่สร้างด้วย doxygen ทำให้เนื้อหาที่ได้มีความแตกต่างเกิดขึ้นในกรณีที่ build path ต่างกัน ก็แก้ไขด้วยการ strip path prefix ออกเสีย

เกี่ยวกับเรื่องพจนานุกรมตัดคำ รุ่นนี้เพิ่มคำเข้ามา 175 รายการ ทำให้จำนวนคำเพิ่มจาก 24,294 คำในรุ่นที่แล้ว เป็น 24,469 คำในรุ่นนี้ โดยคำที่เพิ่ม แบ่งเป็น:

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

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

ในส่วนของ Debian upload นอกเหนือจากการปรับรุ่นของต้นน้ำแล้ว ก็ได้เพิ่ม test suite สำหรับ autopkgtest เพื่อรองรับ CI (Continuous Integration) Test ของ Debian โดยในการอัปโหลดแพกเกจอื่นที่เกี่ยวข้อง เช่น libdatrie ก็จะเพิ่มการรองรับนี้ต่อไปในอนาคตเช่นกัน

ป้ายกำกับ:

29 มิถุนายน 2559

LibThai 0.1.25 : More on Thread-safety

LibThai 0.1.25 ออกแล้ว ความเปลี่ยนแปลงหลักของรุ่นนี้อยู่ที่เรื่อง API ใหม่ที่ thread-safe กว่าเดิม และเรื่องย่อย ๆ คือการแก้ปัญหาการคอมไพล์ด้วย GCC 6 และการปรับพจนานุกรมตัดคำตามปกติ

Thread Safety

ในรุ่น 0.1.23 ได้ทำเรื่อง thread safety ไปแล้วส่วนหนึ่ง จากประเด็นที่พบใน Pango เมื่อมีหลายเธรดพยายามเรียกฟังก์ชันตัดคำพร้อมกัน ทำให้เกิดการแย่งใช้ free list ดังที่เคยอธิบายไว้ใน blog เก่า แต่ก็ยังแก้ไม่หมดจดพอ ดังที่คุณ Mark Brown ได้รายงานมาใน กลุ่มเมล Thai Linux/FOSS developers ว่ายังเหลืออีกจุดหนึ่ง คือขณะเปิดพจนานุกรมเป็นการภายในในการเรียกครั้งแรก เพราะจะยังมีการแย่งกันเปิดพจนานุกรมจนเกิดออบเจกต์พจนานุกรมหลายชุด แม้สุดท้ายจะใช้งานแค่ชุดเดียวและโปรแกรมก็ไม่แครช แต่ออบเจกต์ชุดที่เหลือก็เปลืองเนื้อที่ในหน่วยความจำ และจะไม่ถูกทำลายเมื่อจบโปรแกรมอีกด้วย

วิธีแก้ปัญหาได้พัฒนามาเป็นขั้นเป็นตอนดังนี้ :-

  1. ใช้ mutex ขณะเปิดพจนานุกรม เพื่อให้มีเพียงเธรดเดียวที่เปิด เธรดที่เหลือแค่รอใช้ แต่ปัญหาคือ mutex ของแต่ละ OS จะเรียกไม่เหมือนกัน (บน Linux และ Unix-like OS ทั้งหลาย ใช้ POSIX thread ส่วนวินโดวส์ใช้ Mutex Object ของตัวเอง แม้จะมี pthreads-win32 เป็น wrapper ให้ใช้ แต่ก็ยังต้องสร้างระบบ build และทดสอบขึ้นมาอีก) การจะใช้ mutex ใน libthai จะต้องสร้าง layer ใหม่เพื่อให้ยังคงทำงานข้ามแพลตฟอร์มได้ เป็นงานที่ใหญ่พอสมควรเมื่อเทียบกับปัญหาที่แก้
  2. แยกฟังก์ชันเปิดพจนานุกรมออกมาต่างหาก เป็นแนวคิดที่คุณ Mark Brown ปิ๊งขึ้นมาระหว่างทำอีกประเด็นหนึ่ง คือ การอนุญาตให้ระบุแฟ้มพจนานุกรมที่จะโหลด สำหรับใช้ในกรณีที่ผู้ใช้ไม่ต้องการใช้พจนานุกรมมาตรฐานของ libthai ซึ่งเมื่อกำหนดฟังก์ชันนี้ขึ้นมาแล้ว ก็จะเกิดขั้นตอนใหม่เพิ่มขึ้นก่อนที่ผู้ใช้จะตัดคำ คือการเช็กและเปิดพจนานุกรม ซึ่งผู้ใช้สามารถเรียกใช้ใน critical region ที่มีการล็อคด้วย mutex เองได้ กลายเป็นการยิงปืนนัดเดียวได้นกสองตัว ดังที่คุณ Mark Brown ได้อธิบายมาใน อีกกระทู้หนึ่ง
  3. กำหนด type ThBrk ผมชอบแนวคิดของคุณ Mark Brown ที่ทำให้สามารถเลี่ยงการสร้าง portability layer เพิ่มได้ จึงได้ generalize ออกมาเป็น API ชุดใหม่ คือให้ผู้ใช้สร้างออบเจกต์ชนิด ThBrk (ซึ่งภายในเก็บพจนานุกรมที่เปิดแล้ว) ภายใต้การปกป้องด้วย mutex ในตอนต้น แล้วจึงเรียกฟังก์ชันตัดคำต่าง ๆ แบบขนานตามต้องการ ดังที่ผมได้แสดงความเห็นต่อมาในกระทู้ดังกล่าว คลาส ThBrk นี้ สามารถซ่อนรายละเอียดเพื่อรองรับ implementation แบบอื่นในอนาคตได้ถ้าต้องการ เช่น การใช้สถิติ tri-gram ฯลฯ แต่ในขณะนี้เราจะใช้พจนานุกรมเพียงอย่างเดียวก่อน

API นี้มีการเปลี่ยนชื่อไปมาเพื่อความชัดเจน ในแบบสุดท้าย สรุปว่าตัวอย่างการใช้งานจะเป็นแบบนี้ :-

  // mutex lock here
  ThBrk *brk = th_brk_new (dictpath);
  // mutex unlock here

  // works in parallel
  int pos[N];
  int res = th_brk_find_breaks (brk, str, pos, N);

  // mutex lock here
  th_brk_delete (brk);
  // mutex unlock here

API เดิมจะยังคงมีอยู่เพื่อ backward compatibility โดยจะเป็น wrapper ที่แอบเปิดพจนานุกรมเป็นการภายในเช่นเดิม (ซึ่งไม่ thread-safe) ก่อนเรียก API ชุดใหม่ แต่จะเริ่ม deprecate API เก่าตั้งแต่รุ่นนี้เป็นต้นไป เพื่อให้ผู้ใช้เปลี่ยนไปใช้ API ชุดใหม่แทน ดังนี้ :-

  • th_brk() ให้ใช้ th_brk_find_breaks() แทน
  • th_brk_line() ให้ใช้ th_brk_insert_breaks() แทน
  • th_wbrk() ให้ใช้ th_brk_wc_find_breaks() แทน
  • th_wbrk_line() ให้ใช้ th_brk_wc_insert_breaks() แทน

โดยผู้ใช้ต้องสร้างออบเจกต์ชนิด ThBrk ต่างหากเพื่อส่งให้ฟังก์ชันเหล่านี้ และทำลายเมื่อใช้งานเสร็จ

GCC 6

การปรับโค้ดอีกส่วนหนึ่งเป็นส่วนที่สะสมมาตั้งแต่ช่วงที่ Debian กำหนดให้ใช้ GCC 6 เป็นรุ่น default ที่จะใช้ใน Stretch และได้รับรายงานจากคุณ Martin Michlmayr ใน Debian #811690 ว่าแพกเกจ scim-thai มีปัญหาในการคอมไพล์ด้วย GCC 6 ซึ่งต้นเหตุอยู่ที่ header file หนึ่งของ libthai จึงปรับแก้เสีย

Word Break Dictionary

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

คำที่น่าสนใจที่เพิ่มมาในรุ่นนี้:

  • กรังด์ปรีซ์ : จากดราม่าวอลเลย์บอลหญิงไทย-ญี่ปุ่น
  • สไตรีน, โมโนเมอร์ : จากกรณีถกเถียงเรื่องการใช้กล่องโฟมบรรจุอาหาร
  • ทรัมป์ : จากการเมือง USA ในช่วงนี้
  • แอมเฟตามีน, ปุ๊น : จากข่าวการถอดยาบ้าออกจากบัญชียาเสพติดร้ายแรง
  • สเตอร์ลิง, เบรกซิท : จากข่าวประชามติ UK ถอนตัวจาก EU

และเช่นเคย ส่งเข้า Debian เรียบร้อยแล้วครับ

ป้ายกำกับ:

16 ตุลาคม 2558

LibThai Thread-safety

อันเนื่องมาจากการที่ Pango 1.38.0 ที่ออกมาพร้อมกับ GNOME 3.18 ได้ตัดระบบ dynamic module ทิ้ง แล้วใช้วิธีรวมเข้าในซอร์สโค้ดเลย (GNOME #733882) ทำให้ผู้ดูแลหลัก คือ Behdad Esfahbod ได้มีโอกาสรื้อมอดูลภาษาต่าง ๆ รวมถึงภาษาไทย และพบประเด็นของ libthai ที่รองรับการตัดคำของมอดูลภาษาไทยอยู่ จึงได้ติดต่อมาที่ผมเพื่อให้แก้ไข โดยประเด็นสำคัญคือเรื่อง thread-safety

โค้ดใน libthai มีส่วนหนึ่งที่ทำให้ไม่ปลอดภัยเมื่อเรียกใช้ผ่าน thread หลาย thread พร้อมกัน ซึ่งเป็นส่วนที่ผมใช้ลดปริมาณการเรียก malloc() โดยยังไม่ free() โหนดทางเลือกต่าง ๆ ของการตัดคำที่เลิกใช้แล้วในทันที แต่เอาไปฝากไว้ใน free list เพื่อนำกลับมาใช้ใหม่ และไอ้เจ้า free list ที่เป็นเสมือนโกดังเก็บของเก่ารอ reuse นี่แหละ ที่ implement แบบ static โดยไม่มีการจองก่อนเข้าใช้ ทำให้เมื่อมีหลาย thread เข้าใช้พร้อมกันจะเกิด race condition ขึ้นได้

ประเด็นนี้ pango รุ่นนี้แก้ปัญหาเฉพาะหน้าด้วยการ ล็อคก่อนเรียก th_brk() เพื่อให้ thread ต่าง ๆ เข้าใช้ฟังก์ชันนี้ทีละ thread ซึ่งอาจกลายเป็นคอขวดของระบบหลาย thread ได้ ทางที่ดีคือทำให้ libthai threadsafe เสีย

ทางเลือกที่เป็นไปได้จึงมี 3 ทาง

  1. ยกเลิก free list ไปเสีย (ซึ่ง Behdad สนับสนุน โดยให้เหตุผลว่า malloc() ใน libc รุ่นหลัง ๆ ทำงานเร็วขึ้นมากแล้ว และ CPU สมัยใหม่ก็ทำงานเร็วขึ้นมากแล้วด้วย จะไปนั่งออปติไมซ์ทำไมให้เมื่อยตุ้ม)
  2. ใช้ free list 1 ชุดต่อ 1 thread แทนที่จะใช้ร่วมกันหมด (แต่ละ thread เก็บโหนดที่เลิกใช้ไว้ในกระเป๋า recycle ของใครของมัน ไม่ต้องใช้ร่วมกัน)
  3. ใช้ free list กลางเหมือนเดิม แต่ให้มีระบบ lock (แต่ละ thread ต้องล็อคโกดังขณะเข้าใช้ thread อื่นต้องรอให้ thread ที่ใช้งานอยู่ใช้ให้เสร็จเสียก่อนจึงจะเข้าใช้ได้)

วิธียกเลิก free list อาจจะง่าย แต่ก่อนจะเชื่อก็ต้องวัดดูก่อน ซึ่งจากการทดลองของผมก็พบว่า th_brk() จะทำงานนานขึ้นถึง 18% ถ้าใช้ malloc() ตรง ๆ ทุกครั้ง หรือถ้าเทียบกลับ การใช้ free list สามารถประหยัดเวลาจากการใช้ malloc() ได้ถึง 15% ซึ่งผมถือว่ามีนัยสำคัญ ฉะนั้นจึงไม่เลือกวิธีนี้

จากนั้นจึงมาทดลองวิธีใช้ free list 1 ชุดต่อ 1 thread ซึ่งวิธีการก็ไม่ได้มีอะไรมาก เพียงแค่เปลี่ยนจากการใช้ตัวแปร static มาเป็นการสร้าง free list ใหม่ในการเรียก th_brk() แต่ละครั้งเท่านั้นเอง เนื่องจากตัว libthai เองไม่ได้สร้าง thread ใหม่อยู่แล้ว การสร้าง thread เกิดจากโค้ดผู้เรียกอย่าง pango หรือผู้ที่เรียก pango อีกทีทั้งสิ้น การสร้าง free list ต่อ 1 call ก็เท่ากับการสร้างต่อ 1 thread นั่นเอง

ผลการทดลองคือ free list แบบต่อ thread ก็ยังสามารถประหยัดเวลาได้ถึง 13% (2% ที่หายไปพบภายหลังว่าเกิดจากการสร้าง free list ผิดจากจุดเดิมในโค้ด ทำให้เกิดการสร้างและทำลาย free list เพิ่มขึ้น เมื่อแก้ให้ตรงกับจุดเดิมก็ปรากฏว่าประหยัดเวลาได้เท่าๆ ของเดิม) ซึ่งถือว่าน่าพอใจ

วิธีสุดท้ายที่ใช้ระบบล็อคนั้น จะทำให้ libthai ต้องมีการแทรกโค้ดการเรียก pthread เข้ามา ซึ่งทำให้โค้ดส่วนนี้ดูแปลกแยกไม่สวยงาม และในเมื่อวิธีที่ใช้ free list ต่อ thread ได้ผลเป็นที่น่าพอใจอยู่แล้ว ผมจึงไม่พิจารณาวิธีนี้อีก

จึงได้เป็น rev 577 (และแก้ไขเพิ่มเติมใน rev 583 หลังจากพบสาเหตุที่ทำให้การประหยัดเวลาลดลง 2%)

นอกจากนั้น ก็เป็นความพยายาม optimize การตัดคำของ libthai ต่อ ดังรายการต่อไปนี้:

  • optimize libdatrie ในส่วน AlphaMap (การแม็ปอักขระให้เป็นดัชนีสำหรับ trie transition ซึ่งจะเกิด ทุกครั้ง ที่มี transition) จากการคำนวณหาลำดับของอักขระจากช่วงต่าง ๆ ที่กำหนด มาเป็นการเปิดตารางที่สร้างไว้ล่วงหน้า ฟังดูอาจจะนึกว่ามันคงเร็วขึ้นอย่างมโหฬาร แต่สำหรับ libthai แล้วไม่ใช่ เนื่องจากเรากำหนดช่วงอักขระภาษาไทยเป็นช่วงต่อเนื่องเพียงช่วงเดียว การคำนวณลำดับอักขระจึงไม่ซับซ้อน เพียงแค่ลบด้วยอักขระแรกของช่วงแล้วบวกด้วย 1 ก็จบแล้ว การทำงานที่ประหยัดได้จากการเปิดตารางจึงเป็นเรื่องของโสหุ้ยของการวนลูปเล็ก ๆ น้อย ๆ เท่านั้น ซี่งปรากฏว่าทำให้ตัวฟังก์ชันใช้เวลาทำงานลดลง 14.6% และทำให้การตัดคำของ libthai ใช้เวลาลดลงโดยรวม 0.2% เท่านั้น (libdatrie rev 277) แต่สำหรับ use case ที่อักขระอินพุตมีหลายช่วงไม่ต่อเนื่อง การเปิดตารางนี้จะช่วยประหยัดเวลาได้มาก
  • optimize libthai ในส่วนของการยุบรวมกรณีตัดคำที่ตกตำแหน่งเดียวกัน (การตัดคำของ libthai ใช้วิธีลองตัดคำแบบต่าง ๆ เทียบกับพจนานุกรม แล้วเลือกเอาวิธีที่ได้จำนวนคำน้อยที่สุด โดยใช้การค้นแบบ best-first search ซึ่งคล้ายกับ breadth-first search แต่มีการคำนวณ heuristic ของกรณีต่าง ๆ และยุบรวมกรณีที่คืบหน้าเท่ากันเป็นระยะ ๆ) ซึ่งปรากฏว่าฟังก์ชัน brk_pool_match() ขึ้นชาร์ตฟังก์ชันที่กินเวลานานในรายงานของ Callgrind จึงพยายาม optimize ด้วยการทำให้ตัวฟังก์ชันเร็วขึ้นด้วยการแยกลูปเพื่อลด branching (rev 579 ตัวฟังก์ชันใช้เวลาลดลง 9.3%, runtime โดยรวมลดลง 0.067%) และด้วยการลดปริมาณงานของฟังก์ชันโดยค้นต่อจากจุดเดิมแทนการเริ่มที่ต้นลิสต์เสมอ (rev 582 ตัวฟังก์ชันใช้เวลาลดลง 8.85%, อันดับในชาร์ตเลื่อนลง 2 ขั้น, runtime โดยรวมลดลง 0.0388%) รวมแล้ว ตัวฟังก์ชันใช้เวลาลดลง 17.3% runtime โดยรวมลดลงประมาณ 0.1%

สรุปแล้ว การตัดตำของ libthai โดยรวมใช้เวลาลดลงประมาณ 0.28% (รวมเวลาโหลดพจนานุกรม) พร้อมกับปลอดภัยสำหรับการทำงานหลาย thread โดยไม่ต้องล็อคด้วย

ถ้าไม่มีไอเดียใหม่อีก ก็คงจะออกรุ่นใหม่เร็ว ๆ นี้ครับ

ในอีกด้านหนึ่ง ทีม debian-cd ได้กระทุ้งมาอีกครั้งว่าช่วยทำ udeb ของ libthai ให้หน่อย จะได้ใช้ในโปรแกรมติดตั้งของเดเบียน (Debian #800369) ก็ทยอยทำตั้งแต่ libdatrie1-udeb (0.2.9-3) รอจนผ่าน NEW queue แล้วก็ทำ libthai-data-udeb และ libthai0-udeb ต่อ (ขณะที่เขียน blog ยังรออยู่ใน NEW)

ป้ายกำกับ: ,

03 กันยายน 2558

Thanks

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

  • ปลายเดือนเมษายน 2558
    • ผู้ไม่แสดงตน 1 ท่าน
  • เดือนพฤษภาคม 2558
  • เดือนมิถุนายน 2558
    • คุณธนาธิป ศรีวิรุฬห์ชัย
    • ผู้ไม่แสดงตน 1 ท่าน
  • เดือนกรกฎาคม 2558
    • คุณธนาธิป ศรีวิรุฬห์ชัย
    • ผู้ไม่ประสงค์จะออกนาม
  • เดือนสิงหาคม 2558
  • ต้นเดือนกันยายน 2558

ขอให้ทุกท่านมีความเจริญก้าวหน้าในหน้าที่การงาน คิดสิ่งใดก็ขอให้สมปรารถนานะครับ

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

  • Optimize LibThai/LibDATrie เพิ่มเติม ตรวจสอบความเรียบร้อยทั่วไป และออกรุ่น libdatrie 0.2.9 และ libthai 0.1.22 พร้อมอัปโหลดเข้า Debian (libdatrie 0.2.9-1 และ libthai 0.1.22-1)
  • ตามแก้ปัญหาในแพกเกจ libdatrie-dev และ libthai-dev ที่อัปโหลดไว้ ดังที่มีผู้รายงานบั๊ก Debian #788163 และ Debian #788164
  • ออกรุ่น thaixfonts 1.2.7 ซึ่งได้เตรียมการแก้ปัญหาข้อมูล copyright ที่ขาดไว้ พร้อมทั้งเรื่อง reproducibility ที่ระบบของ Debian ตรวจพบ และแก้ปัญหาเล็ก ๆ น้อย ๆ ในตัว Debian package แล้วอัปโหลดเข้า Debian (thaixfonts 1.2.7-1)
  • ปรับแก้ฟอนต์ในชุด Fonts-TLWG ต่อ เพื่อรองรับภาษามลายูปาตานีเต็มรูปแบบ พร้อมกับเตรียม fallback โดยใช้ภาษาแต้จิ๋วเป็นโจทย์ ดังที่ได้ บันทีกไว้ ซึ่งขณะนี้ยังอยู่ระหว่างดำเนินการต่อ

  • อัปเดตแพกเกจ scim-thai ใน Debian เล็กน้อย เพื่อเป็นส่วนหนึ่งของการย้ายไป GCC5 ของ Debian

  • อัปเดตคำแปลชื่อภาษาใน ISO 639 และ ISO 639-3 ในแพกเกจ iso-codes (มีผลใน iso-codes 3.61)
  • ตรวจทานคำแปล GNOME ตามที่มีผู้ส่งเข้ามา

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

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

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 ก็กำลังเริ่ม ก็คงใกล้ได้เวลาปล่อยของที่ทำสะสมไว้

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

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

ป้ายกำกับ:

21 สิงหาคม 2557

LibThai 0.1.21

LibThai 0.1.21 ออกแล้ว โดยรุ่นนี้ นอกเหนือจากการปรับพจนานุกรมตัดคำตามปกติ ก็ยังมีการเพิ่มสมรรถนะของตัวตัดคำเล็กน้อยด้วย

ตอนที่ยกเครื่องตัวตัดคำของ LibThai เขียนใหม่เมื่อ 8 ปีที่แล้วนั้น (การประเมินผลขณะ merge เข้า trunk) ก็ได้คิดเผื่ออัลกอริทึมแบบอื่นไว้ขณะออกแบบเหมือนกัน กะว่าอาจมาปรับเพิ่มในอนาคต แต่ก็ไม่ได้กลับไปดู จนมาถูกกระตุ้นด้วยการเปิดไฟล์ HTML บางไฟล์ด้วย Firefox/Iceweasel แล้ว พบว่าใช้เวลานาน จึงได้เอาความคิดนี้มาปัดฝุ่นใหม่ โดยพยายาม refactor โค้ดเตรียมรองรับอัลกอริทึมอื่นไว้

และก็ได้คิดออกแบบอัลกอริทึมแบบ longest matching ดู โดยอาศัยโครงจากอัลกอริทึม maximal matching ปัจจุบัน แต่ขณะสำรวจและวิเคราะห์โค้ดเดิม ก็กลับเกิดไอเดียที่จะลดขั้นตอนของโค้ดเดิมขึ้นมาแทน

ผมใช้ callgrind วัดเวลาที่ใช้ในฟังก์ชันต่าง ๆ ก็พบว่าฟังก์ชันที่กินเวลามากที่สุดคือ brk_recover_try() ซึ่งใช้สำหรับหาจุด recover จากคำที่ไม่อยู่ในพจนานุกรม จึงพยายามมุ่งมาลดขั้นตอนในฟังก์ชันนี้

ผมมีสมมุติฐานมากมาย ตั้งแต่การลดการ assign การคัดลอก และการตรวจค่าเล็ก ๆ น้อย ๆ ที่ไม่จำเป็นออก ไปจนถึงการปรับกระบวนการคิดของอัลกอริทึม แล้วก็ต้องโยนทิ้งไปหลายเรื่อง เพราะบางเรื่องเอาเข้าจริงกลับทำให้ใช้เวลาเพิ่มขึ้น มีเพียงเรื่องเดียวที่ทำให้ลดเวลาได้อย่างจริงจัง คือการปรับวิธีตรวจสอบจุด recover จากการ match คล้ายการตัดคำปกติ มาเป็นการ match แบบละโมบ (greedy) โดยพยายาม match คำให้ได้มากคำที่สุดสำหรับแต่ละทางเลือกที่หยิบออกมา ซึ่งมีผลทำให้พบคำตอบได้อย่างรวดเร็วในกรณีที่จุดนั้นสามารถ recover ได้ อีกทั้งไม่ต้องไปเสียเวลาเลือกทางเลือกมาพิจารณาให้มากเกินไป เพราะจุดประสงค์ของการ recover ก็แค่พิจารณาว่าแต่ละจุดสามารถ recover จาก error ได้หรือไม่เท่านั้น ไม่ได้ต้องการ solution ที่สวยงามว่า recover แล้วต้องได้การตัดคำที่ดีที่สุด

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

แนวคิดอื่นที่ยังทำไม่สำเร็จก็เช่น ลดจำนวนการ recover ลง, ลดขนาดของ search space ลง, ทำ cut-off แต่ไว้ค่อยคิดต่อไป รวมถึงการสร้างอัลกอริทึมแบบอื่นด้วย แต่ตอนนี้ขอออกรุ่นที่ปรับสมรรถนะเล็กน้อยนี้ก่อน ให้ทันใช้ใน Jessie ที่กำลังจะ freeze ในเดือนตุลานี้ โดยถือหลัก ออกเนิ่น ๆ ออกถี่ ๆ (release early, release often) เพื่อให้ตัวไลบรารีถูกทดสอบแต่เนิ่น ๆ ด้วย

สำหรับสมรรถนะตัวตัดคำที่เพิ่มขึ้นในรุ่นนี้ วัดเวลาจากกรณีทดสอบโดยใช้ callgrind:

  • ก่อนปรับ: 48,094,350
  • หลังปรับ: 46,893,901

คิดเป็นเวลาที่ลดลง = 2.50%

แต่นี่นับรวมทั้งหมดตั้งแต่เปิดพจนานุกรม, ตัดคำ, ปิดพจนานุกรม ซึ่งเวลาที่ใช้เกี่ยวกับพจนานุกรมนับเป็นสัดส่วนที่มากเอาการอยู่ และเป็น fixed cost ที่เกิดเพียงครั้งเดียวเท่านั้นตลอดโพรเซสที่เรียกตัวตัดคำของ libthai ดังนั้น ผมจึงวัดเวลาที่ใช้ในการเปิด-ปิดพจนานุกรมมาหักลบใหม่:

  • เฉพาะเปิด-ปิดพจนานุกรม: 32,961,393

เมื่อหักลบเวลาเปิด-ปิดพจนานุกรม จะเหลือเวลาสำหรับช่วงตัดคำจริง ๆ คือ:

  • ก่อนปรับ: 15,132,957
  • หลังปรับ: 13,932,508

คิดเป็นเวลาที่ลดลง = 7.93%

หรืออัตราเร็วที่เพิ่มขึ้น = 1 / (1 - 0.0793) - 1 = 0.0861 หรือ 8.61%

ป้ายกำกับ:

11 กุมภาพันธ์ 2557

Thanks, and the November-January Diary

เร่งงานตามสัญญาโครงการอยู่หลายเดือน ไม่ได้เขียน blog จึงขอขอบคุณย้อนหลัง สำหรับผู้สนับสนุนงานพัฒนาของผมในเดือนพฤศจิกายน 2556 ถึงเดือนมกราคม 2557ที่ผ่านมาดังนี้ครับ:

ขออวยพรปีใหม่สากลและตรุษจีนย้อนหลังแด่ผู้สนับสนุนทุกท่าน ขอให้เจริญอายุ วรรณะ สุขะ พละ พร้อมทั้งมั่งมีศรีสุขครับ และที่สำคัญคือ 願源碼與你同在。[หงวงหง่วงแบ้อื่อลื่อตั่งต๋อ] May the Source be with you!

สามเดือนที่ผ่านมา นอกจากงานโครงการอักษรอีสานที่เป็นงานหลักแล้ว ก็มีงานพัฒนาอื่น ๆ เช่น

  • swath 0.5.2
    • แก้ปัญหา infinite loop ใน LaTeX filter ซึ่ง นิวตรอน รายงานมาทาง G+ (เป็น private share) พร้อมแพตช์แก้
    • แก้ปัญหาอักขระหายใน token ยาว ๆ ใน HTML filter ซึ่งคุณ Nicolas Brouard จากโครงการ ดีโมพีเดีย ได้พบขณะใช้ swath ช่วยเตรียมเอกสารฉบับพิมพ์ จากการใช้ base-64 encode รูปภาพในแท็ก <img src="data:image/png;base64,...> ซึ่งทำให้ token ยาวพอจะทำให้พบบั๊กได้ ถือเป็น use case ที่น่าสนใจมาก และทำให้รู้ว่ายังมีผู้ใช้ที่ใช้ HTML filter อยู่ ไม่ใช่แค่ LaTeX
    บั๊กทั้งสองนี้ เกิดจากความพยายามในการป้องกัน buffer overflow ใน swath 0.5.1 แต่แก้ไม่สมบูรณ์ (รุ่นก่อนหน้านั้นจะ segfault ในบั๊กหลังเลยทีเดียว ซึ่งเป็นช่องทางของ buffer overflow exploit ได้)
  • libdatrie 0.2.8
    • แก้ warning ใน test suite
    • แก้ปัญหาที่พบในการใช้ datrie เก็บคีย์ที่เป็นข้อมูล binary ซึ่งจะใช้ alphabet เต็มช่วงตั้งแต่ 1 ถึง 255 ทำให้พบปัญหาคีย์ซ้ำสำหรับอักขระ 255 อันเนื่องมาจากค่าสิ้นสุดการวนลูปที่ไม่ถูกต้อง รายงานโดยคุณ Naoki Youshinaga
    • แก้ให้ trie ล้มเหลวเมื่อเดินด้วยอักขระนอกช่วง alphabet แทนที่จะยอมให้อักขระดังกล่าวเดินได้ด้วยค่า 255 แล้วทำให้เกิดคีย์ปลอม ๆ ขึ้น แนะนำโดยคุณ Naoki Youshinaga เช่นกัน
    • เพิ่มเติมรายละเอียดในเอกสารประกอบ ตามที่ทักท้วงโดยคุณ Naoki Youshinaga
  • ปรับแพกเกจใน Debian โดยการเปลี่ยนแปลงที่น่าสนใจคือ
    • libdatrie-dev และ libthai-dev รองรับ multi-arch แล้ว หลังจากที่รองรับแค่ตัว lib package มาก่อนหน้านี้ ขณะนี้ dpkg รองรับการใช้แฟ้มร่วมกันระหว่างหลายแพกเกจแล้ว ทำให้สามารถใช้ header file ร่วมกันระหว่าง architecture ที่ต่างกันได้
    • รัน test suite ในการ build ทั้งใน libdatrie และ libthai ซึ่งทำให้พบ warning เพิ่มเติมใน libthai และแก้ที่ต้นน้ำแล้ว
  • ร่วมแปล VLC หลังจากพบคำสะกดผิดใน UI ก็ได้แปลเพิ่มเติมด้วยพอสมควร โดยเฉพาะชื่อภาษาต่าง ๆ ทำให้ได้ไปสอบทานกับคำแปล ISO 639 และ ISO 639-3 ในแพกเกจ iso-codes ควบคู่กันไปด้วย (ออกมาในรุ่น 3.50)
  • ตรวจทาน คำแปล GNOME ตามที่มีผู้ส่งคำแปลเข้ามา
  • งานแปล Xfce ขณะนี้แปลได้ 70% แล้ว

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

08 พฤศจิกายน 2556

Thanks, and the September-October Diary

ขอขอบคุณผู้สนับสนุนงานพัฒนาของผมในเดือนกันยายน-ตุลาคมที่ผ่านมาดังนี้ครับ:

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

งานในช่วงสองเดือนที่ผ่านมา แบ่งเป็นหมวด ๆ ดังนี้ครับ:

โครงการอักษรอีสาน

งานพัฒนาที่ LTN และ Debian

เป็นการปล่อยสิ่งที่พัฒนาสะสมมาตั้งแต่ระลอกที่แล้วเมื่อต้นปี โดยทยอยตรวจสอบความเรียบร้อยและออกรุ่นซอฟต์แวร์ต่าง ๆ ดังนี้:

  • libdatrie 0.2.7
    • แก้ไขประเด็นเรื่อง portability เกี่ยวกับ void pointer arithmatics ซึ่งจะมีปัญหากับคอมไพเลอร์ที่ไม่ใช่ GCC โดยได้รับรายงานจากคุณ Mikhail Korobov ว่าคุณ Gabi Daver ได้พบปัญหานี้ขณะคอมไพล์ด้วย Visual C++ พร้อมแพตช์แก้
    • ระหว่างแก้ ได้ทดลองคอมไพล์ด้วยตัวเลือก -Wall ทำให้เจอ warning เพิ่มเติม และแก้ไขจนหมด
    • เขียน test case เพื่อให้สามารถตรวจสอบความถูกต้องเวลาแก้โค้ดได้ในอนาคต ที่ผ่านมาจะทดสอบผ่าน libthai เป็นหลัก แต่เขียน test case เป็นเรื่องเป็นราวน่าจะสะดวกกว่า ซึ่งในระหว่างที่เขียน test case ก็ทำให้ได้อ่านเอกสารประกอบและแก้ไขที่ผิด พร้อมกับได้เพิ่ม API เพื่อความสะดวกในการใช้งานด้วย
    • ปรับ Doxyfile ที่ใช้สร้างเอกสาร เพื่อตัดสิ่งที่เลิกใช้แล้วใน doxygen 1.8.4
    • ออก libdatrie 0.2.7.1 ตามมา หลังจากพบว่าลืมปรับค่า library version เพื่อให้ SONAME สะท้อนการเพิ่ม API ที่เกิดขึ้น
    • อัปโหลด 0.2.7.1-1 เข้า Debian sid
  • thaixfonts 1.2.6
    • มีการปรับระบบ build ตาม autoconf รุ่นใหม่ และเปลี่ยนมาใช้ XZ tarball แทน GZ tarball ซึ่งเป็นสิ่งที่ทำไว้นานแล้ว ก็ออกรุ่นมาเพื่อปรับตามซอฟต์แวร์อื่นเท่านั้น ส่วนตัวเนื้อหาฟอนต์ไม่มีการเปลี่ยนแปลงอะไร
    • อัปโหลด 1:1.2.6-1 เข้า Debian sid
  • LibThai 0.1.20
    • ปรับข้อมูลพจนานุกรมตัดคำตามที่พบกรณีต่าง ๆ ในช่วงที่ผ่านมา [เกร็ด: รุ่นนี้รู้จักอำเภอขนอมที่ไม่ใช่ ขน-อม แล้ว ;-)]
    • แก้ compiler warning ที่พบใน test case ต่าง ๆ
    • อัปโหลด 0.1.20-1 เข้า Debian sid
  • TeX hyphenation patterns
    • sync ข้อมูลพจนานุกรมตัดคำจาก libthai เข้าไปที่ ThaiLaTeX SVN พร้อมกับปรับแก้ hyphenation patterns ตามข้อมูลใหม่
    • แจ้งไปที่โครงการ tex-hyphen ว่าขอปรับข้อมูล hyphenation patterns ภาษาไทย พร้อมกับรายงานปัญหาของสคริปต์บางตัวที่ใช้สร้างข้อมูลอัตโนมัติ คุณ Mojca Miklavec ก็ได้ช่วยแก้สคริปต์ให้ (rev 652, 653) และรับแพตช์ปรับข้อมูลภาษาไทยไปรวมให้ (rev 654)
    • สอบถามและขอ import source ของ hyphenation patterns ภาษาไทยเข้าใน tex-hyphen โดยตรง เพื่อที่ต่อไปจะได้ไปทำงานที่นั่นแทนที่จะต้องผ่าน ThaiLaTeX แบบนี้ ทั้งนี้เพื่อให้เป็นไปตามแผน ที่เคยคุยกันไว้ จนกระทั่งได้ import source ใน rev 655
    • ไม่มีการอัปโหลดอะไรใน Debian แค่รอ Debian อัปเดตแพกเกจ texlive-base เท่านั้น
    • request ขอลบ thailatex ออกจาก Debian unstable เพื่อไม่ให้มีซอร์สตกค้างอยู่ (ลบแล้ว)
  • swath 0.5.1
    • แก้รหัสตัดคำของ Lambda จาก U+200C (ZWNJ) เป็น U+200B (ZWSP) ...ว่าแต่มีใครใช้ฟีเจอร์นี้ไหมเนี่ย?
    • sync ข้อมูลพจนานุกรมตัดคำจาก ThaiLaTeX/hyph-utf8 (ซึ่ง sync มาจาก LibThai อีกที) เพื่อให้ตัวตัดคำ LaTeX ทำงานสอดคล้องกับ hyphenation patterns
    • ก่อนออกก็ปรับซอร์สโค้ดของ swath เพื่อให้แต่ละรุ่นมีการปรับปรุงด้านความปลอดภัยไปทีละน้อย โดยในรุ่นนี้ได้ป้องกัน buffer overflow ใน file filter ต่าง ๆ (ยังมีให้แก้อีกเยอะในรุ่นถัด ๆ ไป :-P )
    • อัปโหลด 0.5.1-1 เข้า Debian sid
  • IBus-LibThai 0.1.2
    • แก้ปัญหาการกด shortcut (เช่น Ctrl-C) ใน IBus 1.5 อันเนื่องมาจากการเชื่อมรวมกับ XKB ของ IBus รุ่นนี้ ทำให้ผังแป้นพิมพ์ที่ระบุใน metadata ของ IBus-LibThai ว่าเป็น th ทำให้กด Ctrl-C ได้เป็น Ctrl-แ เสมอ แก้ไขโดยปรับผังแป้นพิมพ์เป็น us เท่านั้น
    • อย่างไรก็ดี การแก้ปัญหาในรายการที่แล้วทำให้เกิดปัญหาใหม่ คือทำให้กด accelerator ใน GUI ที่แปลเป็นไทย (เช่น Alt-ฟ เพื่อเรียกเมนู แฟ้ม) ไม่ได้ วิธีแก้ที่เหมาะสมจึงควรให้ IBus-LibThai พยายามแปลง key event ที่มีการกดปุ่มประกอบให้เป็นภาษาไทย แต่ปรากฏว่าไม่สามารถส่ง event ที่แปลงแล้วกลับไปหา event queue ได้ เนื่องจากฟังก์ชัน ibus_engine_forward_key_event() ไม่ทำงานอย่างที่คาด งมอยู่นานก็ไม่สามารถแก้ได้ เวลามีจำกัดจึงใช้วิธีกำหนดผังแป้นพิมพ์เป็น us,th เพื่อให้ GTK+ กับ XKB ไปคุยกันเอง ซึ่งก็ได้ผล แต่ปัญหาคือ มันจะแปลงอักขระตามผังเกษมณีเท่านั้น ใครใช้ผังปัตตะโชติใน IBus-LibThai ก็จะงง ไว้หาวิธีแก้ต่อไปในรุ่นหน้า
    • เพิ่มการรองรับการป้อนเลขไทยด้วยแป้นตัวเลข โดยอาศัยการกด CapsLock ล็อคไว้ หรือใช้การยกแคร่ระดับ 3 (Alt ขวา) อนึ่ง ตามที่เคยได้ ออกแบบไว้ เมื่อสองปีก่อนนั้น จะใช้ ScrollLock ไม่ใช่ CapsLock เนื่องจาก CapsLock จะไปเพิ่มขั้นตอนขณะสลับภาษาไปเป็นภาษาอังกฤษที่จะต้องปลด CapsLock อีกขั้นหนึ่งด้วย แต่ในครั้งนี้ได้ตัดสินใจเปลี่ยนเป็น CapsLock ด้วยเหตุผลสองประการ ประการแรกคือการตรวจสอบสถานะของ ScrollLock ด้วย API ของ IBus เป็นไปได้ยาก เพราะไม่มีการเตรียมการรองรับไว้ ประการที่สองคือในแป้นพิมพ์ย่อส่วน เช่นแป้นพิมพ์โน้ตบุ๊ก หลายรุ่นได้ตัดปุ่ม ScrollLock ออกไปแล้ว ตามที่ วิกิพีเดียว่าไว้ (โน้ตบุ๊กผมก็ไม่มี)
    • อัปโหลด 0.1.2-1 เข้า Debian sid

งานแปล

  • ตรวจทาน คำแปล GNOME ตามที่มีผู้ส่งคำแปลเข้ามา โดยที่ผมไม่ได้แอคทีฟตามแปลเองอีกต่อไปแล้ว
  • แปล Xfce เป็นไทย เพิ่มเติม โดยล่าสุด ได้แปล core package ต่าง ๆ ครบแล้ว พร้อมกับปรับคำแปลทั้งหมดจาก master กลับไปที่ branch xfce-4.10 ด้วย และแปลปลั๊กอินที่ผมใช้อีกนิดหน่อยเพิ่มเติม ทำให้ขณะนี้อัตราการแปลของภาษาไทยอยู่ที่ 54% แล้ว

blog นี้ก็เลยยาวหน่อย ขอขอบคุณทุกท่านที่ติดตามครับ

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

04 กรกฎาคม 2556

Thanks, and the June Diary

เดือนมิถุนายนที่ผ่านมา อ.พฤษภ์ บุญมา ได้หย่อนสตางค์ลงหมวกเพื่อสนับสนุนงานพัฒนาของผม ขอขอบคุณอีกครั้งครับ

สำหรับเดือนมิถุนายนที่ผ่านมา งานหลักก็ยังคงเป็นโครงการอักษรอีสานยืนพื้น โดยมีการตัดสินใจลงมือกระทำบางอย่าง คือ

  • ตั้งกลุ่มอักษรอีสานใน Facebook เพื่อเรียนเชิญอาจารย์ผู้เชี่ยวชาญอักษรอีสานมาช่วยให้ข้อแนะนำ หลังจากที่ผมได้พบผู้รู้ทาง Facebook มาหลายท่านก่อนหน้านี้
  • ตัดสินใจใช้รหัสยูนิโค้ด U+0324 COMBINING DIAERESIS BELOW แทนตัวสะกดแม่กกจุดคู่ในอักษรธรรมอีสานไปพลางก่อน โดยปรับเพิ่มทั้งในฟอนต์โคตรบูรณ์ และ ระบบป้อนข้อความล้านช้าง พร้อมกันนี้ก็ได้ปรับปรุงร่างข้อเสนอขอเพิ่มอักขระอักษรธรรมเพื่อให้อ้างอิงได้สะดวกในตัวเองด้วย
  • ทดลองใช้ระบบวิราม (virama) สำหรับตัวเฟื้องของอักษรไทน้อยที่ยืมมาจากอักษรธรรม โดยทดลองใช้กับการปริวรรตหนังสือใบลานเรื่อง พญาคันคาก ปรากฏว่า Pango/Harfbuzz มันวาดให้ใน GTK+ ขณะเตรียมข้อมูลก็จริง แต่ Firefox ไม่ยอมวาดตัวเฟื้องบนเว็บให้ คาดว่าระบบจัดแสดงข้อความของ Firefox คงมีการกลั่นกรองอักขระยูนิโค้ดเข้มงวด ทำให้ได้แนวทางว่าควรจะถอยมาใช้วิธีกำหนดอักษรเป็นตัว ๆ ตามแนวทางเดิมของอักษรไทย-ลาวไปพลางก่อน เมื่อได้ข้อกำหนดที่เป็นมาตรฐานสุดท้ายแล้วค่อยมาปรับแก้ตามมาตรฐานทีหลัง
  • ตัดลำดับ SAKOT + อ อักษรธรรมออกจากฟอนต์โคตรบูรณ์ เพราะเป็นลำดับที่ไม่ถูกต้อง แต่ผู้ใช้มักจะชอบใช้มากกว่าจะใช้สระออล่าง (U+1A6C) ที่ถูกต้อง เพื่อป้องกันการเกิดข้อมูลที่ผิดมาตรฐาน จึงตัด fallback นี้ออก
  • ปรับวิธี map ปุ่มในระบบป้อนข้อมูลล้านช้าง จากเดิมที่ map อักขระ ASCII ไปเป็นอักษรธรรม มาเป็นการ map จากตำแหน่งปุ่มโดยตรง เนื่องจากการ map จาก ASCII จะมีปัญหาเมื่อผู้ใช้พยายามสลับผังแป้นพิมพ์ด้วย XKB แทนการสลับ IBus engine โดยในผังแป้นพิมพ์ไทยจะยังมีบางปุ่มที่เป็นอักขระ ASCII อยู่ เช่น / - , . ? แต่พอผู้ใช้พยายามกดอักขระเหล่านี้ในผังแป้นพิมพ์ไทย ล้านช้างจะแปลงอักขระเหล่านี้เป็นอักษรธรรมเสีย ซึ่งไม่ใช่สิ่งที่ผู้ใช้ต้องการ
  • ปรับโค้ดใน IBus-LibThai ในทำนองเดียวกันด้วย (ผมเคย blog ไปแล้ว ว่า IBus-LibThai สำหรับภาษาไทยนั้น เกิดขึ้นได้ด้วยแรงกระตุ้นจากโครงการอักษรอีสาน แม้การพัฒนาในระยะต่อมาก็ยังเป็นจริงอยู่ IBus-LibThai นั้น เข้า Debian และ Ubuntu ไปแล้ว แต่ IBus-Lanxang ยังต้องรอความชัดเจนอีกสักหน่อยเกี่ยวกับตัวมาตรฐาน)

งานอื่นที่เกิดขึ้นในเดือนมิถุนายน:

  • ปรับคำแปล ISO 3166-2 ในโครงการ iso-codes โดยอ้างอิงหนังสือแผนที่ภูมิศาสตร์ ทำให้คำแปลปรับจาก (732 translated, 218 fuzzy, 3749 untranslated) ไปเป็น (1829 translated, 58 fuzzy, 2812 untranslated)
  • edit แผนที่ OSM ในกรุงเทพฯ และขอนแก่น อันเป็นควันหลงจากงาน INET Bangkok 2013 และ Netizen Meetup โดยได้รับความร่วมมือจากสมาชิก KKLUG ด้วย

สำหรับเดือนกรกฎาคมนี้ ก็คงจะเดินหน้าโครงการอักษรอีสานต่อไปครับ โดยจะไปเน้นที่อักษรไทน้อยมากขึ้น

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

19 กุมภาพันธ์ 2556

IBus-LibThai 0.1.0

ผมเคย เขียนถึง IBus-LibThai ไปเมื่อ 3 เดือนก่อน ซึ่งเป็นการปัดฝุ่นโค้ดที่ร่างไว้ตั้งแต่ปีก่อนจนใช้การได้ แล้วก็ย้ายไปทำ โครงการล้านช้าง สำหรับป้อนอักษรธรรมลาว-อีสานผ่าน IBus อยู่ระยะหนึ่ง ระหว่างนี้ก็ทดลองใช้เองมาเรื่อย ก็ใช้การได้ดี แต่เนื่องจากมีงาน release ซอฟต์แวร์ต่าง ๆ ก็เลยยังไม่ได้กลับไปดูต่อ

จนกระทั่งในการประชุม “โสเหล่” ครั้งล่าสุดของ KKLUG ผมถูกกระตุ้นอีกครั้งจาก เสกสรร เพื่อนร่วมโครงการ อักษรอีสาน ว่าเขาใช้ IBus แล้วทำให้มีปัญหาการป้อนภาษาไทยด้วย ibus-m17n หลายอย่าง ไหน ๆ ผมก็ปล่อยซอฟต์แวร์ที่คั่งค้างอยู่เสร็จไปแล้ว ก็เลยรับปากว่าจะเร่งออก IBus-LibThai ให้

วันนี้ก็ได้แล้วครับ IBus-LibThai 0.1.0 ซึ่งต้องอาศัย API ใหม่ใน LibThai 0.1.19 ที่เพิ่งออกไป โดยได้ทำแพกเกจสำหรับ Debian และอัปโหลดเข้า experimental เรียบร้อยแล้วด้วย ขณะนี้รออยู่ที่ คิว NEW รอทีม ftp-master มารีวิวก่อนจึงจะได้เข้า อาจจะรอนานหน่อย เพราะช่วงนี้สำหรับ Debian แล้ว งานออกรุ่น Wheezy สำคัญกว่าครับ

ป้ายกำกับ: ,

29 มกราคม 2556

LibThai 0.1.19

LibThai 0.1.19 ออกแล้ว โดยในรุ่นนี้ นอกจากการปรับปรุงพจนานุกรมตัดคำตามปกติแล้ว ก็ยังมีการเพิ่ม API สำหรับการตรวจแก้ลำดับการพิมพ์ใน input method คือ th_validate_leveled() ตามความต้องการที่เกิดขึ้นขณะพัฒนา ibus-libthai ด้วย

อธิบายสักเล็กน้อย เดิมนั้น API th_validate() ใช้สำหรับวิเคราะห์การตรวจแก้ลำดับการพิมพ์ โดยรับอาร์กิวเมนต์ขาเข้าแค่สองตัว คือเซลล์ก่อนหน้าเคอร์เซอร์ กับอักขระใหม่ที่เพิ่งกดเข้ามา แล้วจะคืนรายละเอียดการตรวจแก้มาในอาร์กิวเมนต์ที่สาม เกณฑ์การตรวจลำดับ ก็จะใช้ค่าคงที่ตายตัวเป็นแบบ strict เสมอ แต่ใน ibus-libthai มีเครื่องมือตั้งค่าเลือกระดับการตรวจลำดับได้ (pass through, basic, strict) จึงเกิดความคิดว่าควรให้การตรวจแก้สามารถกำหนดระดับการตรวจได้ด้วย จึงต้องเพิ่ม API th_validate_leveled() ที่รับอาร์กิวเมนต์อีกตัวเป็นค่าระดับการตรวจด้วย และ th_validate() เดิม ก็จะกลายเป็นเพียงกรณีเฉพาะของ API ใหม่นี้

เพื่อรักษาความเข้ากันได้กับซอฟต์แวร์เดิมที่ยังเรียกใช้ th_validate() ตัวเก่าอยู่ จึงใช้วิธีเพิ่ม API แทนที่จะแก้ API ซึ่งในอนาคตอาจค่อย ๆ เลิกใช้ API เก่าอีกที

และแน่นอนว่ารุ่นนี้ออกโดยใช้ tarball แบบบีบอัดด้วย XZ ตามประกาศ LTN แล้ว ซึ่งขนาด tarball ที่ได้เล็กลงถึง 30%:

-rw-r--r--  1 thep thep 537508 มิ.ย.  12  2012 libthai-0.1.18.tar.gz
-rw-r--r--  1 thep thep 373420 ม.ค.  29 12:47 libthai-0.1.19.tar.xz

อัปโหลด 0.1.19-1 เข้า Debian experimental แล้วด้วยครับ รอ Wheezy ออกแล้วค่อยย้ายเข้า Sid อีกที การออกรุ่น libthai นี้ เป็นขั้นแรกของการทำ ibus-libthai เพราะจะทำให้สามารถ build ibus-libthai ได้ พร้อมกันนี้ก็ได้ file ITP สำหรับ ibus-libthai รอไว้แล้วด้วย รอทิ้งช่วงสักระยะเพื่อให้ชุมชน Debian ได้พิจารณาก่อนที่จะออกรุ่นจริงแล้วทำแพกเกจต่อไป

ป้ายกำกับ:

02 พฤศจิกายน 2555

IBus LibThai

แผนการหนึ่งที่ผมคิดจะทำมานานแล้ว คือการเชื่อมต่อ LibThai เข้ากับ IBus ซึ่งเป็น input method framework แบบใหม่ที่กำลังจะมาแทนที่ระบบเดิมแทบทั้งหมด ไม่ว่าจะเป็นรุ่นเก๋ากึ๊กอย่าง XIM หรือทางลัดของ toolkit อย่าง GTK+ IM module หรือ Qt4 IM module หรือจะเป็นระบบที่ยังอายุไม่มากอย่าง SCIM หรือ uim ทั้งหมดจะค่อย ๆ ถูก IBus แทนที่ในดิสโทรต่าง ๆ เช่น สำหรับ Fedora ซึ่งเป็นต้นน้ำนั้น ก็ใช้ IBus โดยปริยายแล้ว และ Debian เองก็เตรียมจะใช้ IBus เป็นตัวหลักเช่นกัน

ทั้งนี้ การเชื่อมรวมกับ IBus ก็เป็นเป้าหมายของ GNOME มาระยะหนึ่ง และสำเร็จในรุ่น 3.6 นี้เอง

แนวโน้มแบบนี้ ทำให้ผมปรารภกับเพื่อน ๆ ในแวดวงมาตั้งแต่ปีก่อนแล้วว่าคงต้องพยายามให้มี input method ภาษาไทยใน IBus แล้วแหละ แต่ก็ชะลอมาเรื่อย ด้วยเหตุผลต่าง ๆ คือ หนึ่ง ผมมีเวลาทำงานน้อย เนื่องจากต้องสลับไปทำหลายงาน, สอง หาเอกสารของ IBus ไม่เจอ (เพิ่งเจอ Developer guide เมื่อเร็ว ๆ นี้ หลังจากที่ได้งมแกะซอร์สของ engine อื่นเป็นตัวอย่างไปเยอะแล้ว), สาม มอดูล ibus-m17n ที่ไปใช้ input method จากคลังของ m17n ก็พอใช้การได้ มีภาษาไทยมาเรียบร้อย ซึ่งผมเคยคุยกับคุณทาคาฮาชิที่ ETL ซึ่งเป็นผู้ดูแลทางเมลและร่วมทดสอบจนมันใช้การได้ สามารถแก้ลำดับการป้อนได้ด้วย (ถ้า app รองรับ) ความเร่งด่วนที่จะต้องเขียน engine ตัวใหม่ก็ลดลง

ผมทดลองใช้ IBus มาระยะหนึ่ง โดยเฉพาะตอนที่ไปเรียนภาษาจีนเมื่อต้นปี ต้องการป้อนภาษาจีน จึงเป็นตัวกระตุ้นให้ใช้ IBus เป็นหลัก แต่รู้สึกไม่พอใจกับ input method ภาษาไทยของ ibus-m17n เลย เนื่องจากมันตอบสนองช้ามากในหลาย ๆ กรณี เช่น เมื่อใช้กับเว็บที่มี Javascript หนัก ๆ อย่าง facebook หรือ Google+ อีกทั้งการพยายาม fallback ไปใช้ pre-edit string โดยอุบัติเหตุก็น่ารำคาญ เนื่องจากอักขระตัวสุดท้ายจะสูญหาย ซึ่งบั๊กนี้ดูเหมือนจะแก้ไปแล้ว แต่มันก็ยังกลับมาเป็นพัก ๆ อยู่ดี

หลังจากที่ห่างหายจากการป้อนภาษาจีนมาระยะหนึ่ง ผมจึงแอบกลับมาใช้ gtk-im-libthai ตามเดิม เพื่อความรวดเร็วของงาน จนกระทั่งถูกกระตุ้นอีกครั้งหนึ่งจากโครงการอักษรอีสาน ที่ทีมงานกำลังร่วมกันออกแบบ input method ของอักษรธรรม และเริ่มศึกษา framework ที่จะใช้สร้างต้นแบบ ซึ่งตัวเลือกอันดับต้น ๆ ก็คือ IBus นั่นเอง

นั่นแหละ ผมถึงได้กลับมาปัดฝุ่นโค้ด ibus-libthai ที่ร่างไว้ตั้งแต่ปีกลาย เอามาปรับต่อจนใช้การได้เมื่อเช้านี้เอง

ตัวโค้ด อยู่ที่ SVN ของ LTN ซึ่งสามารถ checkout มาทดสอบได้:

$ svn co http://linux.thai.net/svn/software/ibus-libthai/trunk \
  ibus-libthai

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

ป้ายกำกับ: ,

18 มิถุนายน 2555

ThaiLaTeX 0.5.0 et al

fine-tune แล้ว แถม super-tune อีก โดยผมได้ทดลองใช้กับเอกสารจริงควบคู่กันมาตลอดด้วย ก็ได้เวลาเข็นทุกสิ่งที่ได้พัฒนาสะสมมาสู่ผู้ใช้เสียที

เริ่มจาก LibThai 0.1.18 ก่อน โดยในระหว่างปรับแก้พจนานุกรม hyphenation ของ ThaiLaTeX ก็ทำให้พบคำสะกดผิดในพจนานุกรม จึงกลับไปแก้พจนานุกรมตัดคำของ LibThai ด้วย เมื่อรวมกับการเพิ่มคำใหม่ ๆ ในพจนานุกรมตามปกติ (สำหรับรอบนี้ก็เช่นคำว่า ชยันตี จากงานฉลองพุทธชยันตีที่ผ่านมา) ก็ทำให้ได้เป็น LibThai รุ่นล่าสุดนี้ โดยรายการคำที่เพิ่มทั้งหมด ก็ได้ sync เข้าไปที่ ThaiLaTeX ตาม work flow ที่เตรียมไว้แล้วด้วย

จากนั้น ก็ตามมาด้วย swath 0.4.3 ซึ่งได้ถอดเปลี่ยนพจนานุกรมตัดคำมาใช้ชุดเดียวกับที่ ThaiLaTeX ใช้ เพื่อให้จุดแบ่งคำพอดีกันกับ hyphenation pattern ที่จะมาทำงานต่อ ทั้งนี้ได้ให้ swath ดึงพจนานุกรมมาจาก ThaiLaTeX แทนที่จะดึงจาก LibThai โดยตรง เนื่องจาก ThaiLaTeX อาจมีรายการบางรายการที่ปรับเปลี่ยนเพิ่มเติมตามความเหมาะสมของการทำ hyphenation บ้าง (ซึ่งความจริงแทบไม่มีเลย)

สำหรับ swath นี้ ความจริงยังมีงานพัฒนาบางส่วนที่ผมยังทำค้างไว้ใน local branch ในเครื่องผม เกี่ยวกับการแปลงรหัสอักขระ แต่ยังไม่ได้ merge เข้ามาในรุ่นนี้ ไว้ทำเสร็จจริง ๆ แล้วค่อย merge สำหรับรุ่นหน้าต่อไป

สุดท้าย ก็เป็นพระเอกของชุดนี้ คือ ThaiLaTeX 0.5.0 ซึ่งได้ขึ้น minor version เลขใหม่ อันเนื่องมาจากฟีเจอร์ใหม่ที่เพิ่มขึ้นอย่างมีนัยสำคัญ โดยก็ได้อัปโหลดไว้ที่ CTAN (พร้อม TDS installation image) ด้วย

สำหรับ ThaiLaTeX นี้ ถ้าใครติดตั้งจากซอร์สจะมีขั้นตอนที่ต้องแก้เองด้วยมือหลังจาก make install ด้วย เนื่องจากแต่ละระบบมีระบบจัดการ config เรื่อง hyphenation ไม่เหมือนกัน ซึ่งผมได้ทดลองเพียง Debian unstable และ Fedora 17 เท่านั้น รายละเอียดได้เขียนบันทึกไว้ใน README.hyphen ในซอร์สแล้ว

และแน่นอน ทั้งสามรายการนี้ ได้อัปโหลดเข้า Debian unstable แล้ว บางคนอาจจะเห็นและได้ใช้ไปบ้างแล้ว ส่วน Ubuntu ก็รอพบได้ในรุ่น 12.10 ต่อไป

ความจริงที่พยายามเร่งออกในช่วงนี้ก็เพื่อให้ทันกำหนด Wheeze freeze ที่ใกล้จะถึงนี้ด้วย

แถมท้ายนิดหนึ่ง ว่าก่อนหน้านั้นได้มีการออก scim-thai 0.1.3 ที่ได้ปรับเอา API ที่เลิกใช้แล้วใน GTK+ 3 ออกไป เนื่องจาก GTK+ 3 รุ่นใหม่นี้จะไม่ยอมให้คอมไพล์ผ่านอีกแล้ว ตามที่ได้รับรายงานใน Debian #676060

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

28 มิถุนายน 2554

scim-thai 0.1.2

scim-thai 0.1.2 ออกไปแล้วเมื่อวานนี้ ก็คงเป็นตัวสุดท้ายในการปรับรุ่นแพกเกจต่าง ๆ ใน linux.thai.net ระลอกนี้ (แต่ไม่ใช่หมดแค่นี้ อาจมีการออกรุ่นตัวอื่นที่ได้ปรับรุ่นไปแล้วอีกถ้าจำเป็น)

สำหรับ scim-thai รุ่นนี้ ถือเป็น maintenance release คือไม่ได้มีการเพิ่มความสามารถใหม่อะไร แต่เป็นการแก้ปัญหาเล็ก ๆ น้อย ๆ เกี่ยวกับระบบ build ที่ตรวจพบในช่วง 3 ปีที่ผ่านมา กล่าวคือ

  • ปรับการ config การแปลข้อความด้วย gettext จาก gettextize ให้มาใช้ autopoint แทน ซึ่งทำให้ปรับข้อมูล PO ได้แนบเนียนไร้ร่องรอยมากขึ้น
  • แก้ปัญหาการคอมไพล์ในกรณีที่ libthai ไม่ได้ติดตั้งไว้ในพาธมาตรฐาน เป็นปัญหาที่พบในระหว่างพัฒนา libthai ซึ่งต้องติดตั้งแยกต่างหากจาก libthai ของระบบ แล้วทำให้พบว่า make rule ของ scim-thai ยังติดปัญหาการคอมไพล์กับ libthai ที่แยกติดตั้งต่างหากนี้อยู่ กลายเป็นกรณีตัวอย่างให้อุดรูรั่ว
  • แก้ปัญหาเรื่องการกำหนด LDFLAGS ซึ่งพบระหว่างที่จะ build แพกเกจ Debian รุ่นใหม่โดยปรับการ build ให้ใช้แฟล็ก "-Wl,--as-needed" แล้วพบว่าแฟล็กนี้ไม่มีผล ตรวจสอบแล้วพบว่าเป็นปัญหาการใช้ target *_LDFLAGS ใน Makefile.am ในการเพิ่มไลบรารีให้กับมอดูล ซึ่งพอสคริปต์ configure กำหนด LDFLAGS=-Wl,--as-needed แล้ว แฟล็กนี้มันจะไปต่อท้ายค่าใน target *_LDFLAGS อีกที ซึ่งผมดันไปลิสต์ไลบรารีในนั้นเสียแล้ว ค่า LDFLAGS จากการ configure ก็เลยไปต่อท้ายอยู่หลังรายชื่อไลบรารี จึงไม่มีผลกับไลบรารี ที่ถูกแล้ว ต้องใช้ target *_LIBADD ในการเพิ่มไลบรารีแทน ชื่อไลบรารีจึงจะมาอยู่หลัง LDFLAGS ในคำสั่งคอมไพล์ เป็นปัญหาที่มาดูทีหลังก็งง ว่าตอนนั้นผมทำแบบนั้นไปได้อย่างไร ทำให้ต้องมางมอยู่นานกว่าจะเจอ

สำหรับผู้ที่สนใจเรื่องการใช้แฟล็ก --as-needed ของลิงเกอร์ ก็ขออธิบายสักหน่อย

แฟล็ก --as-needed ใช้เพื่อให้ object file ที่ได้นั้น ลิงก์เฉพาะกับไลบรารีที่มีการใช้ symbol โดยตรงเท่านั้น (ตรวจสอบการลิงก์ได้ด้วยคำสั่ง objdump -p {so/exe-file} แล้วดูที่บรรทัด NEEDED ทั้งหลายใน Dynamic Section)

กล่าวคือ สมมติว่า object file ของเรามีการเรียกใช้ libA และ libB โดย libA ไปลิงก์กับ libP, libQ อีกที เวลาลิงก์จะต้องใส่ไลบรารีทั้งหมดที่ใช้ คือเป็น "-lA -lB -lP -lQ" และลิงเกอร์ก็จะลิงก์ทุกไลบรารีเข้ามาใน object file ของเรา นี่คือกรณีปกติ

การลิงก์ดังกล่าวมีปัญหาในทางปฏิบัติ เพราะไลบรารีต่าง ๆ มีการต่อยอดกันเป็นทอด ๆ หลายชั้น เช่น เฉพาะ GTK+ ก็มีไลบรารีลูกต่อพ่วงมาอีกถึง 44 ตัว:

$ ldd /usr/lib/libgtk-3.so.0.0.10 | wc -l
44

ในนี้ก็มีตั้งแต่ pango, glib, ati, cairo, freetype, libxcb และพรรคพวกอีกมากมาย และการลิงก์เฉพาะ GTK+ เข้ามาในโปรแกรมของเรา ก็จะต้องลิงก์ไลบรารีเหล่านี้เข้ามาทั้งหมด ทั้งที่ไม่มีการเรียกใช้ฟังก์ชันโดยตรงเลยก็ตาม

ความจริง ตอน run-time ก็จะต้องโหลดไลบรารีเหล่านี้เข้ามาทั้งหมดอยู่แล้ว ไม่งั้น GTK+ ก็ทำงานไม่ได้ อ้าว แล้วถ้างั้นมีปัญหาอะไร?

ปัญหาก็คือ การลิงก์ตรงกับไลบรารีใด ๆ จะหมายถึงการเกิด dependency ระหว่างแพกเกจของไลบรารีที่ลิงก์กันนั้น ทำให้ระบบแพกเกจโดยรวมมี dependency จำนวนมโหฬารเกินความจำเป็น และจะเป็นอุปสรรคต่อการขยับไลบรารีหนึ่ง ๆ ขึ้นสู่ major version ใหม่ (ซึ่งอาจไม่ ABI compatible กับรุ่นเก่า) เพราะจะต้องตามไป rebuild แพกเกจทั้งหมดที่ลิงก์ตรงกับมันอยู่ กลายเป็นปริมาณงานที่มากเกินจำเป็น ในเมื่อโปรแกรมคุณแค่ใช้ฟังก์ชันของ GTK+ เท่านั้น ทำไมจะต้องไป rebuild เมื่อมีการปรับ major version ของ cairo หรือ freetype ด้วย? สู้ให้เรื่องพวกนี้มัน encapsulate อยู่ในแพกเกจ GTK+ เท่านั้นไม่ดีหรือ?

นี่จึงเป็นที่มาของการใช้แฟล็ก --as-needed ในการ build แพกเกจของ distro ต่าง ๆ เพื่อให้มีการลิงก์เฉพาะกับไลบรารีที่มีการใช้ symbol โดยตรงเท่านั้น แล้วเวลาโหลดหรือติดตั้ง มันก็จะทำงานเป็นทอด ๆ ตามลำดับ dependency เอง ถ้ามีการปรับ major version ก็จะ rebuild เฉพาะแพกเกจที่มีการเรียกใช้ symbol โดยตรงเท่านั้น

บางคนอาจถามว่า แล้วทำไมไม่แค่ลิงก์แบบ "-lgtk-3-0" กับแพกเกจที่ใช้ GTK+ ก็พอล่ะ? ไม่ต้องแถมพ่วงพวก pango, glib ฯลฯ มาด้วยก็ได้ คำตอบคือการลิงก์แบบนี้ไม่ portable ครับ ลิงเกอร์ของ GNU อาจทำได้ แต่ยูนิกซ์ทั่วไปทำไม่ได้ ต้องใส่ library ที่ต้องการตามลำดับให้ครบด้วยจึงจะลิงก์ผ่าน (เข้าใจว่ามาจากวิธีลิงก์แบบ static ที่ต้องมีไลบรารีครบ) ดังนั้น พวก link flags ที่ได้จาก pkg-config จึงมีไลบรารีที่ต้องใช้แถมพ่วงมาทั้งหมดเสมอ แล้วลิงเกอร์ของ GNU สามารถมาคัดออกอีกทีด้วยตัวเลือก --as-needed ได้

ป้ายกำกับ: ,

19 มิถุนายน 2554

gtk-im-libthai 0.2.0

ออก gtk-im-libthai 0.2.0 ไปแล้วเมื่อวานซืน แต่เพิ่งมีเวลาได้เขียนบันทึกวันนี้เอง

รุ่นนี้มีการเปลี่ยนแปลงที่สำคัญ คือพอร์ตโค้ดไปยัง GTK+ 3 โดยจะ build และติดตั้งมอดูลสำหรับทั้ง GTK+ 2 และ GTK+ 3 ตามแต่จะ configure โดยใช้ซอร์สเดียวกัน ทำให้รุ่นนี้ต้องการ GTK+ 2 รุ่น 2.21.8 ขึ้นไป ที่มี macro ใหม่ของ GTK+ 3 โดยยังคง compatibility กับ GTK+ 2 รุ่นเก่าไว้อยู่

นอกจากนี้ก็ได้ปรับปรุงระบบ build ใหม่ให้จัดการการติดตั้ง/ถอดถอนได้รัดกุมขึ้น และเปลี่ยนมาใช้แมโครรุ่นใหม่ ๆ ของ autotools

การเปลี่ยนแปลงต่าง ๆ เหล่านี้ถือว่าใหญ่พอสมควร จึงได้ออกรุ่นใหม่เป็น 0.2.0 แทนที่จะเป็น 0.1.6

นอกจากนี้ ก่อนออกรุ่นยังได้พบบั๊กอีกตัวหนึ่งในระหว่างพัฒนา solution สำหรับการป้อนเลขไทยด้วย numpad แทนการใช้ ฟอนต์ Sarabun IT9 คือการไม่รับปุ่มที่เป็น level 3 shift จึงได้แก้ไข พร้อมกับรายงานบั๊ก GNOME #652720 ใน Thai-Lao IM module ของ GTK+ ซึ่งมีอาการเดียวกันไว้ด้วย (มี deb ที่แก้แล้วที่ debclub repository [gtk+2.0, gtk+3.0]) ซึ่งหลังจากแก้แล้วก็สามารถใช้ผังแป้นพิมพ์ใหม่ที่ผมดัดแปลงให้ป้อนเลขไทยด้วย numpad ได้ ส่วนรายละเอียดของผังแป้นพิมพ์ใหม่นั้น ขอเขียนแยกใน blog ต่างหากครับ

ผู้ใช้ Debian sid ก็คงได้พบการ upgrade ไปแล้วเมื่อวานนี้ โดยถ้าคุณใช้ GNOME 3 จาก experimental อยู่ ก็อาจจะติดตั้งแพกเกจใหม่เพิ่ม คือ gtk3-im-libthai เพื่อให้ใช้กับ GTK+ 3 ได้ โดยแพกเกจเดิม (gtk-im-libthai) นั้น จะเป็นมอดูลสำหรับ GTK+ 2.x เท่านั้นครับ

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

25 มีนาคม 2554

LibThai 0.1.15

LibThai 0.1.15 ออกไปแล้วเมื่อวานนี้ รุ่นนี้ไม่มีการเปลี่ยนแปลงในส่วนโค้ด มีแต่การปรับข้อมูลพจนานุกรมที่ใช้ตัดคำตามปกติ

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

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

แหล่งข้อมูลที่ผมได้มาก็เช่น:

  • ข่าวประจำวันในหนังสือพิมพ์ ทั้งออนไลน์และออฟไลน์ (ส่วนใหญ่จะออนไลน์) ทำให้ได้ศัพท์การเมือง ชื่อเมืองในข่าวต่างประเทศ ศัพท์ธุรกิจ ชื่อยี่ห้อสินค้า ศัพท์แฟชั่น วงการบันเทิง ไลฟ์สไตล์
  • เว็บต่าง ๆ ในอินเทอร์เน็ต
  • หัวข้อสนทนาใน social network จะพบคำในสาขาต่าง ๆ ที่หลากหลาย และจากคำเสนอแนะเพิ่มเติมของเพื่อน ๆ
  • อีเมลส่วนตัว เช่น เพื่อนบางคนเปิดเบเกอรี่ ก็จะพูดถึงรายการขนมต่าง ๆ ทำให้ได้ชื่อขนมใหม่ ๆ มาเรื่อย ๆ แม้เจ้าตัวก็ยังไม่รู้ ว่ากำลังช่วยพัฒนา libthai อยู่ :-)
  • ป้ายสินค้า ฉลากสินค้า ป้ายร้าน ป้ายโฆษณา ฯลฯ

พูดง่าย ๆ คือ แทบทุกอย่างรอบตัวสามารถเป็นแหล่งข้อมูลให้ libthai ได้ แต่การจะเพิ่มคำเข้าในพจนานุกรม ก็จะต้องมีการกลั่นกรองเสียก่อนว่าคุ้มหรือไม่ บางคำเป็นคำที่ใช้เฉพาะกลุ่มมาก ๆ ก็ยังไม่เพิ่ม คำที่มีตัวสะกดหลากหลายก็จะพยายามเพิ่มคำที่เป็นมาตรฐานไว้ก่อน โดยมีสมมุติฐานว่าโอกาสที่ผู้คนจะสะกดตรงตามมาตรฐานมีมากกว่าแบบย่อย ๆ ที่แต่ละคนสะกดแบบของตัวเอง แต่ถ้ามีการสะกดผิดจากมาตรฐานไปในทางเดียวกันมากพอก็จะเพิ่มแบบย่อยที่มีความถี่สูงสุดอันดับต้น ๆ ไว้ โดยตัดสินจากการใช้ search engine ตรวจสอบจำนวนเอกสารที่พบ

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

ความจริงยังมีแผนบางอย่างในการปรับโครงสร้างโค้ดของ libthai เหมือนกัน แต่ต้องหาเวลาว่างให้ได้ก่อน ที่ผ่านมาก็มีงานอื่นมากแทรกมากมายนับตั้งแต่ MiniDebCamp 2010 เป็นต้นมา ทำให้ร้างราจากการทำงานกับซอฟต์แวร์ที่ LTN มาเป็นแรมปี รวมถึงกับ Debian เองด้วย blog ก็เขียนน้อยลงมาก นี่เดี๋ยวก็ต้องกลับไปเร่งงานที่รับมาแบบกึ่ง full-time ต่อ แต่ขอออก thaifonts-arundina อีกตัวให้เสร็จก่อน

ป้ายกำกับ:

hacker emblem