1 điểm bởi GN⁺ 2025-03-05 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trên IBM PS/1 486-DX2 66MHz, một PC hiệu năng cao năm 1993, DOOM gốc đạt 21,5fps với demo1, còn fastDOOM đạt 30,1fps trong cùng điều kiện, cho thấy khác biệt trong tối ưu hóa bản port DOS
  • fastDOOM bắt đầu từ PCDOOM v2, kết hợp lõi Linux DOOM, I/O của Heretic, APODMX và I/O đồ họa Mode Y dựa trên dịch ngược assembly của DOOM.EXE
  • Kết quả build và benchmark 52 bản phát hành cùng 3.042 commit cho thấy mức tăng hiệu năng không đến từ một compiler hiện đại duy nhất, mà từ sự tích lũy của các tối ưu hóa nhỏ
  • Các cải thiện lớn gồm bỏ qua render thanh trạng thái, inline FixedDiv, tối ưu duyệt BSP, bỏ qua render visplane, loại bỏ tham chiếu gián tiếp qua pointer, và tách file thực thi theo từng renderer
  • Chế độ đồ họa có lợi hay bất lợi tùy CPU và bus; với CPU chậm thì Mode Y có lợi, còn trong môi trường CPU nhanh và VLB/PCI thì Mode 13h hoặc VESA direct có thể tốt hơn, nhưng vẫn còn các ràng buộc như VESA 2.0

Khác biệt hiệu năng lộ rõ trên 486-DX2

  • Khi chạy DOOM gốc bằng doom.exe -timedemo demo1 trên mẫu IBM PS/1 486-DX2 66MHz “Mini-Tower” 2168, kết quả là 1710 gametics in 2783 realtics
    • DOOM không xuất fps trực tiếp, nên phải tính bằng 1710/2783*35; kết quả là 21,5fps
  • Trên cùng máy đó, chạy fdoom.exe -timedemo demo1 cho kết quả 1710 gametics in 1988 realtics, xác nhận 30,1fps
  • Với các map nặng hơn như demo1 của doom2, tốc độ tăng từ 16,8fps của bản gốc lên 24,9fps của fastDOOM, nhanh hơn 48%
  • Tuy nhiên, hỗ trợ joystick và network gameplay đã bị loại bỏ, nên đây không phải là bản port giữ nguyên toàn bộ chức năng

Dòng phả hệ mã nguồn tạo ra fastDOOM

  • DOOM ban đầu được phát triển trên NeXT Workstation, và có cấu trúc dễ port khi phần lớn mã lõi được tách khỏi một hệ thống con I/O nhỏ
  • Bản DOS thương mại dùng DOS I/O do id Software viết, nhưng do phụ thuộc vào DMX, một proprietary sound library, nên không thể được open-source nguyên trạng vào năm 1997
  • Mã được công bố là phiên bản Linux do Bernd Kreimeier chỉnh lý trong lúc thực hiện dự án sách giải thích engine
  • Phiên bản DOS PCDOOM v2 được tái cấu trúc từ tổ hợp sau
    • Lõi của Linux DOOM
    • I/O của Heretic
    • APODMX để mô phỏng DMX
    • I/O đồ họa i_ibm.c được reverse-engineer từ bản dịch ngược assembly của DOOM.EXE
  • fastDOOM lấy chính PCDOOM v2 này làm điểm xuất phát

Theo dõi thay đổi hiệu năng qua release và commit

  • Victor “Viti95” Nieto phát hành fastDOOM thường xuyên, gắn tag cho từng release, và quản lý sao cho một commit làm một việc
  • Lịch sử Git của fastDOOM gồm 3.042 commit, nhờ đó có thể theo dõi thay đổi hiệu năng theo từng commit
  • 52 bản phát hành fastDOOM, PCDOOM v2 và DOOM.EXE gốc được tải về; một chương trình Go tạo RUN.BAT để chạy -timedemo demo1, rồi mount bằng mTCP NETDRIVE để kiểm thử
    • Điều kiện là DOOM.WAD, bật sound, screen size 10
    • Toàn bộ bộ test được chạy 5 lần và fps trung bình được vẽ thành biểu đồ
  • Dù PCDOOM v2 được build bằng OpenWatcom 2, mức cải thiện so với DOOM.EXE gốc không lớn, nên khó giải thích mức tăng của fastDOOM chỉ bằng việc dùng compiler hiện đại
  • Biểu đồ kích thước file cho thấy công việc ban đầu hướng tới làm nhẹ hơn bằng cách dọn dẹp và xóa mã

Các thay đổi theo release đã kéo hiệu năng lên

  • Nếu timedemo toàn bộ 3.042 build, có thể mất khoảng 9 ngày, nên benchmark theo từng commit được thực hiện chủ yếu quanh các bản có mức tăng tốc lớn: v0.1, v0.6, v0.8, v0.9.2, v0.9.7
  • fastDOOM v0.1

    • v0.1 gồm 220 commit
    • Patch lớn nhất là e16bab8 ở build 36
      • “Crispy optimization” biến việc render thành no-op khi phần trăm trên thanh trạng thái không đổi, tránh render scrap buffer và blit lên màn hình
      • Chỉ riêng thay đổi này đã được xác nhận làm tăng 2fps
    • a9359d5 ở build 167 inline FixedDiv thành macro
    • 9bd3f20 ở build 207 đưa tối ưu từ PSX Doom vào để cải thiện cách duyệt BSP
    • dc0f48e ở build 212 inline hàm render bề mặt ngang R_MakeSpans
    • Trong toàn bộ commit, có 100 commit xóa mã, tức khoảng một nửa là xóa code
  • fastDOOM v0.6

    • v0.6 gồm 33 commit
    • 22819fd ở build 342 bỏ qua việc render visplane không cần thiết
    • 40e0d4b ở build 359 và ccd296f ở build 360 giảm tham chiếu gián tiếp qua player pointer
    • f29e665 ở build 369 inline screenspace line splitter
  • fastDOOM v0.8

    • v0.8 gồm 282 commit
    • Do sound system không ổn định, timedemo được chạy không có sound rồi hiệu chỉnh fps
    • Release này tập trung vào text-mode renderer, và đã xảy ra hồi quy khi Crispy optimization bị bỏ ở build 670 và build 730
    • Các cải thiện chính như sau
      • f279b7d: tách file thực thi theo từng renderer như FDOOM.EXE, FDOOM13H.EXE
      • 1874ee8: vô hiệu hóa debugging của compiler
      • 6aae724: khôi phục Crispy optimization
      • 1366ebf: chỉ compile ít mã hơn khi có thể
  • fastDOOM v0.9.2 và v0.9.7

    • v0.9.2 có 110 commit; các thay đổi chính là tối ưu so sánh skyflatnum, tối ưu R_DrawColumn cho Mode Y, và dọn dẹp mã R_DrawSpan
    • v0.9.7 được giới thiệu là có 293 commit, nhưng output lệnh cho thấy 294 commit
    • Dù benchmark release này nhiều lần, vẫn không thể giảm nhiễu
    • Các thay đổi chính gồm thử nghiệm thay đổi x86 ASM, lựa chọn CPU cho 386SX và tối ưu CR2, tối ưu ESP cho R_DrawSpan386SX, mã dựa trên ASM fuzz column rendering, loại bỏ phép so sánh CMP trong mỗi vòng lặp, v.v.

Tiêu chí chọn Mode 13h, Mode Y và VESA direct

  • fastDOOM khám phá nhiều tối ưu hóa cho các tổ hợp CPU và video bus đa dạng như 386, 486, Pentium, Cyrix cùng ISA, VLB, PCI
  • Trên IBM PS/1 486-DX2 66MHz, tối ưu dùng Mode 13h thay cho Mode Y lại chậm hơn
  • Mode 13h

    • Phần cứng xử lý việc phân phối dữ liệu vào bốn VRAM bank của VGA, nên với CPU nó trông như một framebuffer tuyến tính 320x200 duy nhất
    • Không thể double buffering trong VRAM, nên phải buffer trong RAM rồi chép lại sang VRAM, tức ghi byte hai lần
    • Engine phải bị block ở VSYNC
  • Mode Y

    • Có thể truy cập riêng từng VGA bank, cho phép VRAM triple buffering, và có thể ghi byte trực tiếp vào VRAM chỉ một lần
    • Việc chọn bank cần lệnh OUT chậm
    • Nếu ghi đồng thời vào hai VGA bank thông qua latch, có thể sao chép pixel theo chiều ngang để có low-detail mode
    • Render Specter vô hình cần readback từ VRAM nên chậm hơn nhiều
    • Theo giải thích của John Carmack, DOOM dùng một interleaved planar mode tương tự Mode X trong chế độ VGA 320×200×256, và luân phiên ba display page
    • Texture mapping trực tiếp vào video memory có thể mang lại thêm 10%~15% tốc độ trên nhiều video card
    • Page flip không bị tearing cũng khả thi hơn so với buffering trong main memory
    • Heretic ra mắt năm 1994, và do thay đổi phần cứng thời đó khiến Mode 13h trở thành lựa chọn hấp dẫn hơn, Raven đã sửa DOOM engine theo hướng này
    • fastDOOM cung cấp cho người dùng nhiều file thực thi
    • FDOOM.EXE
    • FDOOM13H.EXE
    • FDOOMVBD.EXE
  • Mode 13h và VESA direct của fastDOOM

    • Mode 13h của fastDOOM render vào một framebuffer duy nhất trong RAM, rồi sao chép sang VRAM sau khi toàn bộ cảnh hoàn tất
    • Không ép VSYNC, nên có thể xuất hiện flickering
    • Trên bus ISA 8-bit chậm, differential copy chỉ truyền các pixel đã thay đổi được dùng
    • Trên các bus nhanh hơn như ISA 16-bit, VLB, PCI, toàn bộ backbuffer được sao chép bằng REP MOVS
    • Theo các thử nghiệm của Viti95, chế độ tốt nhất cho CPU 486 là FDOOMVBD.EXE, tức VESA direct mode cho 320×200
      • Kết hợp ưu điểm của Mode Y với mã render đã tối ưu của Heretic
      • Tránh lệnh OUT, ngoại trừ một lần OUT mỗi frame khi chuyển buffer
      • Cần card đồ họa VLB hoặc PCI có LFB được bật và hỗ trợ VESA 2.0; chậm trong low-detail và potato-detail mode
    • IBM 2168 không hỗ trợ VESA 2.0, nên sẽ phát sinh lỗi khi chạy FDOOMVBP.EXEFDOOMVBD

Những thử nghiệm không hiệu quả và ấn tượng tổng thể

  • Các flag theo từng bộ xử lý của OpenWatcom là 4r/4s3r/3s cũng đã được thử, nhưng bị dừng lại
    • Cả flag 386 và flag 486 của wcc386 đều được thử, và kết quả là phiên bản 386 luôn có vẻ nhanh hơn
  • Viti95 cũng có mục tiêu đổi compiler của fastDOOM từ OpenWatcom v2 sang DJGPP, tức GCC
    • Lý do là trên cùng source, GCC cho thấy tạo ra mã nhanh hơn
    • Hoặc việc OpenWatcom v2 được cải thiện để thu hẹp khoảng cách hiệu năng cũng vẫn là một lựa chọn đáng mong muốn
  • Hiệu năng của fastDOOM là kết quả của việc tận dụng các cải tiến có sẵn từ Crispy, PSX, GBA, Lee Killough và bổ sung hàng loạt tối ưu hóa mới
  • Ý tưởng và một phần mã của Ken Silverman cũng được đưa vào các hàm render cho UMC Green CPU, tạo ra mức tăng tốc lớn trên phần cứng đó
  • fastDOOM không phải là kết quả của một thay đổi ma thuật duy nhất, mà là ví dụ về một bản port DOS nhanh hơn DOOM gốc nhờ hàng nghìn tối ưu hóa nhỏ tích lũy

1 bình luận

 
GN⁺ 2025-03-05
Ý kiến trên Hacker News
  • Đây là một ví dụ hay cho thấy nút thắt thường không nằm ở nơi ta dự đoán, nên cần profiling và đo đạc
    Việc render phần trăm trên thanh trạng thái nghe có vẻ khá hiển nhiên với một chuyên gia hiểu rõ cấu trúc của Doom, nhưng trước đó chắc hẳn không ai đoán nó là nút thắt

    • Chẳng hạn, từng có trường hợp một ứng dụng không rõ vì sao dùng 60% CPU và 25% GPU, hóa ra là do một animation CSS nhỏ
      https://www.granola.ai/blog/dont-animate-height
    • Tôi nhớ đến một ca tối ưu trong Super Mario World cho SNES, nơi việc hiển thị điểm của người chơi cực kỳ kém hiệu quả và ngốn khoảng 1/6 thời gian mỗi frame
      Trong trường hợp tệ nhất, việc hiển thị điểm trên thanh trạng thái dùng gần 1/6 toàn bộ frame, khiến ngưỡng bắt đầu bị chậm thấp hơn; bản vá đã đổi cách lưu và hiển thị điểm thành gần như thời gian hằng số, thậm chí còn nhanh hơn một chút so với trường hợp tốt nhất trước đây
      https://www.smwcentral.net/?p=section&a=details&id=35746
    • Với góc nhìn của lập trình viên game, những kiểu chậm như vậy khá phổ biến
      Render UI có thể gây chết người vì độ trong suốt, layering, vẽ lại, đặc biệt là do kích hoạt cấp phát bộ nhớ; cách so sánh giá trị cũ và mới trước khi vẽ lại giúp ích rất nhiều
      Trong CSS cũng từng có dự án mà layer và độ trong suốt là nút thắt, khi đó điểm mấu chốt là giảm số lượng layer
    • Ví dụ tôi thích nhất là vấn đề thời gian tải của GTA Online: cuối cùng hóa ra là do xử lý sai một file JSON 10MB, và một người ngoài tổ chức đã lần ra
      Họ đã giảm thời gian tải 6 phút xuống dưới 2 phút
      https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...
    • Tôi nhớ đến vụ npm từng chậm gấp 2 lần chỉ vì thanh tiến trình terminal hào nhoáng
      https://news.ycombinator.com/item?id=10974929
  • Có lẽ tôi không phải độc giả mục tiêu thật sự, nhưng NETDRIVE của mTCP khá thú vị
    Tôi không ngờ từ thời xa xưa như vậy đã có lựa chọn network storage dùng được; tìm thử thì thấy https://www.brutman.com/mTCP/mTCP_NetDrive.html, thật sự rất hay
    NetDrive được nói là một device driver cho phép DOS truy cập image đĩa từ xa do máy khác host như một thiết bị cục bộ gắn với drive letter

    • Ở phòng máy trường học đầu thập niên 90, khoảng 25 máy Mac Plus được nối kiểu daisy-chain qua AppleTalk tới một máy Mac II, và tất cả máy Plus đều mount filesystem từ Mac II
      Nó chậm khủng khiếp, khiến học sinh mất 5–10 phút đầu giờ chỉ để mở trình xử lý văn bản; Xerox Alto cũng dùng network mount cho drive
      Khi đã có networking, chẳng mấy chốc sẽ có người muốn sao chép file, và cách tiện nhất là làm cho nó trông như filesystem cục bộ
      DOS không có networking tích hợp nên tụt hậu ở mảng này, và phải tự làm rất nhiều thứ
    • Có một mẹo gọn gàng là boot DOS qua mạng từ iSCSI target bằng iPXE
      Nó cho phép truy cập đọc/ghi vào thiết bị block qua mạng mà không cần driver hay cấu hình phía DOS, nhưng nếu dùng như share thì sẽ hỏng rất nhanh, vì nó gần với block device hơn
      Nghe như phép thuật, nhưng có vẻ iPXE patch BIOS để chuyển truy cập đĩa sang iSCSI
    • Tôi tò mò liệu thời DOS có NAS hay WebDAV mount không
      Những thứ như FTP và telnet thì chắc chắn có, nhưng tôi muốn biết remote mount có thực sự được dùng không, hay là bất khả thi vì băng thông thấp
  • Thread GitHub có Ken Silverman tham gia đúng là một kho báu
    Thật đáng kinh ngạc khi thấy tác giả FastDOOM và Ken đào sâu vào các thanh ghi 486 khó hiểu và hiệu quả chu kỳ clock
    Thật vui khi vẫn có người chăm chút để Doom tiếp tục được cải thiện hiệu năng

    • Đã rất lâu rồi tôi mới nhớ lại KenS; hồi thập niên 90, ông ấy hoạt động rất tích cực trong cộng đồng modding Duke3D
      Phần scripting đó đúng nghĩa là lần “lập trình” đầu tiên của tôi, và theo một nghĩa nào đó, tôi nợ KenS toàn bộ sự nghiệp và tài sản của mình, thật tuyệt
    • Năm ngoái, khi đang làm một engine render 2.5D tương tự, tôi đã email cho Ken Silverman về một phần ít được biết đến của Build Engine, và ông ấy trả lời như thể mới hôm qua vẫn còn làm việc với nó
    • Bên trong đó có những viên ngọc thật sự
      Tôi đặc biệt thích ý tưởng dùng CR2 và CR3 như các thanh ghi scratchpad trên 386SX, nơi truy cập bộ nhớ rất chậm, và 386DX không có cache
      Kỹ thuật dùng ESP làm bộ đếm vòng lặp mà không tắt interrupt cũng thiên tài: nó đảm bảo ESP luôn trỏ tới một vị trí stack hợp lệ
  • Một tính năng không được bàn nhiều ở đây trong FastDOOM là đủ loại chế độ video kỳ quặc
    Chế độ văn bản IBM MDA: https://www.youtube.com/watch?v=Op2tr2lGK6Y
    EGA & Plantronics ColorPlus: https://www.youtube.com/watch?v=gxx6lJvrITk
    CGA xanh dương-hồng cổ điển: https://youtu.be/rD0UteHi2qM
    CGA 320x200x16 dùng hack ‘ANSI from Hell’: https://www.youtube.com/watch?v=ut0V1nGcTf8
    Hercules: https://www.youtube.com/watch?v=EEumutuyBBo
    Hầu hết có vẻ chạy chậm hơn VGA vì các việc như remap màu

    • Sẽ tốt nếu hỗ trợ cả Tandy Graphics Adapter
      Nếu phải chạy bằng CGA thì chắc tôi không thích, và nếu chiếc Tandy 1000 TL/2 của tôi vẫn còn sống thì có lẽ cần một bản build 286 cho nó
    • Thật sự rất hay, và cho thấy rõ vì sao những khía cạnh như thế này của game nên được tách riêng
      Nó làm tôi nhớ đến Clean Architecture “hiện đại” trong các ứng dụng backend
    • Chế độ văn bản IBM MDA thật kinh khủng... nên lại hay
  • Tôi ấn tượng với đoạn “IBM PS/1 486-DX2 66Mhz, ‘Mini-Tower’, model 2168. Chiếc máy tính tôi luôn muốn có khi còn tuổi teen nhưng không thể mua được”
    Khoảng năm 1992, tôi đang dùng chiếc PC thứ tư tự lắp, và các hội chợ máy tính KCS ở Marlborough MA là một nguồn tài nguyên khổng lồ cho dân mày mò
    Tôi mua linh kiện để dựng PC, dùng một thời gian rồi bán đi, rồi lại mua linh kiện tiếp
    Đến cuối năm 1992, tôi đang chạy 486-DX3 100 gắn đồng xử lý toán học ULSI 487, và trong một thời gian có thể nói đó là chiếc PC nhanh nhất trong khuôn viên trường, thậm chí có lẽ là máy tính nhanh nhất
    Nó nhanh hơn nhiều mẫu Pentium và không mắc lỗi toán học
    Bản build cuối cùng được biện minh vì tôi phải mô phỏng một nhà máy đồng phát điện khí/diesel trong bảng tính Excel 21 trang cho luận văn danh dự, khiến thời gian tính lại quá lâu
    Chuyên ngành của tôi là khoa học môi trường, nhưng toàn bộ sự nghiệp lại đi vào máy tính

    • Tôi không chắc đây có phải chuyện để “wow” không
      Có nhất thiết phải làm khó một người không thể mua nổi chiếc máy tính như vậy vào năm 1992 không
      Hơn nữa không có thứ gọi là “DX3”, và DX4, chiếc 486 đầu tiên 100MHz, ra mắt vào tháng 3/1994, nên chuyện đã chạy nó vào cuối năm 1992 có vẻ không đúng
      Chiếc máy tính đầu tiên của nhà tôi, nếu bỏ qua một chiếc XT đã quá lỗi thời nhận được khoảng năm 1992, là 66MHz 486-DX2 mua đầu năm 1995
      Ngay cả mấy chục năm sau, thấy ai đó khoe một chiếc máy tính bất khả thi nhanh hơn máy của tôi dù có lợi thế đi trước 3 năm vẫn làm tôi thấy hơi tự ái một cách kỳ lạ
    • Chắc chắn gợi lại nhiều ký ức
      Khoảng năm 1992 tôi là sinh viên nghèo, vay khoảng 2.000 đô la từ credit union để mua một chiếc 486 DX2-50
      Tính theo tiền ngày nay thì tôi đã chi hơn 4.000 đô la cho một chiếc máy khá cơ bản, và tôi dual boot DOS với Linux trên nó
    • 486DX mà lại có 487 ư, tôi tưởng 487 chỉ hữu ích với chip SX
      Tìm thử thì thấy 487 tiêu chuẩn vốn là một 486DX hoàn chỉnh, vô hiệu hóa và thay thế 486SX
      Hay đó là một loại đồng xử lý tuyệt vời nào khác mà tôi không biết?
    • “Nhanh hơn nhiều mẫu Pentium và không mắc lỗi toán học” thì đúng là có quyền khoe
      Đó là thắng áp đảo thật sự
  • Không chỉ phát hành thường xuyên, Viti95 còn thể hiện kỷ luật Git xuất sắc: mỗi commit làm một việc và mỗi release đều được gắn tag
    https://fabiensanglard.net/fastdoom/#:~:text=one%20commit%20...

  • Tôi không hiểu Ibuprofen trong câu “trước khi biết fastDOOM, tôi đã cam chịu rằng phải uống Ibuprofen khi chơi” nghĩa là gì

    • Có lẽ là nói đến đau đầu do tốc độ khung hình thấp
  • Nếu tác giả có đọc được, trong toàn bộ tài liệu họ của John Carmack bị gõ nhầm thành “Carnmack”

  • Nếu nhìn một cách không cay độc về lý do phần mềm hiện đại chậm và không có các tối ưu kiểu này, có thể giải thích bằng giả thuyết chuẩn hóa/tối ưu hóa
    Khi một thứ trở thành chuẩn, tối ưu hóa sẽ theo sau
    Vì người ta sẽ muốn nhanh nhất trong khi vẫn vượt qua mọi bài kiểm thử của chuẩn
    Doom giờ cũng đã trở thành một game chuẩn được port lên mọi nơi, từ CPU mới đến lò nướng bánh, và các giao thức email hay các chuẩn trình duyệt như WebRTC, QUIC cũng tương tự
    Lý do các web app hiện đại hay ứng dụng Electron không nhanh là vì chúng đang ở giai đoạn khám phá
    Chúng được cập nhật mỗi ngày theo nhu cầu người dùng mới, và về hiệu năng thì chỉ cần đủ nhanh để không gây cản trở là được
    Vì vậy ứng dụng IRC rất nhanh, còn Slack và Teams thì gần như chắc chắn luôn chậm

  • Việc quay lại từng phiên bản để kiểm tra cải tiến và hồi quy không hề đơn giản
    Một số tối ưu hóa có thể tạo ra bug chỉ được phát hiện về sau, và những tính năng cần thiết cũng có thể làm giảm hiệu năng
    Vì vậy nếu có kiểm thử hiệu năng chạy tự động trước mỗi release thì cuộc sống sẽ dễ thở hơn, và khi phát hiện vấn đề hiệu năng, chỉ cần viết regression test như thường lệ
    Điều muốn nói cuối cùng vẫn là: hãy kiểm thử hiệu năng