เหตุใดจึงใช้เวลาสูงสุด 30 วินาทีในการสร้างกลุ่มแถว CCI แบบง่าย


20

ฉันกำลังทำงานเกี่ยวกับการสาธิต CCIs เมื่อฉันสังเกตเห็นว่าเม็ดมีดบางรุ่นของฉันใช้เวลานานกว่าที่คาดหมาย คำจำกัดความของตารางที่จะทำซ้ำ:

DROP TABLE IF EXISTS dbo.STG_1048576;
CREATE TABLE dbo.STG_1048576 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_1048576
SELECT TOP (1048576) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

สำหรับการทดสอบฉันกำลังแทรกแถว 1048576 ทั้งหมดจากตาราง staging เพียงพอที่จะเติมกลุ่มแถวที่ถูกบีบอัดได้หนึ่งกลุ่มตราบใดที่มันไม่ได้ถูกตัดด้วยเหตุผลบางประการ

ถ้าฉันใส่จำนวนเต็มทั้งหมด mod 17000 มันใช้เวลาน้อยกว่าหนึ่งวินาที:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 17000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

เวลาดำเนินการของ SQL Server: เวลา CPU = 359 ms, เวลาที่ผ่านไป = 364 ms

อย่างไรก็ตามหากฉันใส่จำนวนเต็ม mod เดียวกัน 16000 บางครั้งก็ใช้เวลามากกว่า 30 วินาที:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

เวลาดำเนินการของ SQL Server: เวลา CPU = 32062 ms, เวลาที่ผ่านไป = 32511 ms

นี่คือการทดสอบซ้ำที่ทำบนเครื่องหลายเครื่อง ดูเหมือนว่าจะมีรูปแบบที่ชัดเจนในเวลาที่ผ่านไปเมื่อการเปลี่ยนแปลงค่า mod:

MOD_NUM TIME_IN_MS
1000    2036
2000    3857
3000    5463
4000    6930
5000    8414
6000    10270
7000    12350
8000    13936
9000    17470
10000   19946
11000   21373
12000   24950
13000   28677
14000   31030
15000   34040
16000   37000
17000   563
18000   583
19000   576
20000   584

หากคุณต้องการที่จะเรียกใช้การทดสอบด้วยตัวคุณเองรู้สึกอิสระที่จะปรับเปลี่ยนรหัสการทดสอบที่ผมเขียนนี่

ฉันไม่พบสิ่งที่น่าสนใจใน sys.dm_os_wait_stats สำหรับตัวดัดแปลง 16000:

╔════════════════════════════════════╦══════════════╗
             wait_type               diff_wait_ms 
╠════════════════════════════════════╬══════════════╣
 XE_DISPATCHER_WAIT                        164406 
 QDS_PERSIST_TASK_MAIN_LOOP_SLEEP          120002 
 LAZYWRITER_SLEEP                           97718 
 LOGMGR_QUEUE                               97298 
 DIRTY_PAGE_POLL                            97254 
 HADR_FILESTREAM_IOMGR_IOCOMPLETION         97111 
 SQLTRACE_INCREMENTAL_FLUSH_SLEEP           96008 
 REQUEST_FOR_DEADLOCK_SEARCH                95001 
 XE_TIMER_EVENT                             94689 
 SLEEP_TASK                                 48308 
 BROKER_TO_FLUSH                            48264 
 CHECKPOINT_QUEUE                           35589 
 SOS_SCHEDULER_YIELD                           13 
╚════════════════════════════════════╩══════════════╝

ทำไมแทรกสำหรับID % 16000ใช้เวลานานกว่าที่แทรกสำหรับID % 17000?

คำตอบ:


12

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

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

  • การเข้ารหัสค่า (การปรับและ / หรือการแปลค่าให้พอดีกับบิตจำนวนเล็กน้อย)
  • การเข้ารหัสพจนานุกรม (การอ้างอิงจำนวนเต็มกับค่าที่ไม่ซ้ำกัน)
  • เรียกใช้การเข้ารหัสความยาว (การจัดเก็บการรันค่าที่ซ้ำกันเป็นคู่ [ค่านับ])
  • บรรจุบิต (เก็บสตรีมในบิตน้อยที่สุด)

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

การจับเคสที่ช้าด้วย Windows Performance Recorder และวิเคราะห์ผลลัพธ์ด้วย Windows Performance Analyzer เราจะเห็นว่าเวลาการประมวลผลส่วนใหญ่ถูกใช้ไปในการดูการจัดกลุ่มข้อมูลสร้างฮิสโตแกรมและตัดสินใจว่าจะแบ่งพาร์ทิชันอย่างไรให้ดีที่สุด เงินฝากออมทรัพย์:

การวิเคราะห์ WPA

การประมวลผลที่แพงที่สุดเกิดขึ้นสำหรับค่าที่ปรากฏขึ้นอย่างน้อย 64 ครั้งในกลุ่ม นี่คือฮิวริสติกเพื่อกำหนดว่าRLE บริสุทธิ์น่าจะมีประโยชน์หรือไม่ เคสที่เร็วขึ้นส่งผลให้เกิดการจัดเก็บข้อมูลที่ไม่บริสุทธิ์เช่นการแสดงแบบบิตเต็มไปด้วยขนาดการจัดเก็บข้อมูลสุดท้ายที่ใหญ่ ในกรณีไฮบริดค่าที่มีการซ้ำ 64 ครั้งหรือมากกว่านั้นจะถูกเข้ารหัส RLE และส่วนที่เหลือจะถูกบีบอัดเป็นบิต

ระยะเวลาที่ยาวที่สุดเกิดขึ้นเมื่อจำนวนสูงสุดของค่าที่แตกต่างกับการทำซ้ำ 64 ครั้งปรากฏในส่วนที่เป็นไปได้ที่ใหญ่ที่สุดคือ 1,048,576 แถวที่มีค่า 16,384 ชุดที่มี 64 รายการแต่ละรายการ การตรวจสอบรหัสเผยให้เห็นขีด จำกัด เวลาแบบตายตัวสำหรับการประมวลผลที่มีราคาแพง สามารถกำหนดค่านี้ในการใช้งาน VertiPaq อื่น ๆ เช่น SSAS แต่ไม่สามารถใช้ใน SQL Server ได้เท่าที่ฉันสามารถบอกได้

เข้าใจบางอย่างในการจัดเรียงจัดเก็บข้อมูลขั้นสุดท้ายสามารถซื้อได้โดยใช้ที่ไม่มีเอกสารDBCC CSINDEXคำสั่ง สิ่งนี้แสดงส่วนหัว RLE และรายการอาร์เรย์บุ๊กมาร์กใด ๆ ลงในข้อมูล RLE และสรุปโดยย่อของข้อมูลบิตแพ็ค (ถ้ามี)

สำหรับข้อมูลเพิ่มเติมดู:


9

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

ครั้งแรกที่ฉันพยายามแปรจำนวนแถวที่แทรกเข้าไปใน CCI TOPโดยใช้ ฉันใช้ID % 16000สำหรับการทดสอบทั้งหมด ด้านล่างนี้เป็นกราฟเปรียบเทียบแถวที่แทรกกับขนาดเซ็กเมนต์ของ rowgroup ที่ถูกบีบอัด:

กราฟขนาดเทียบกับขนาดสูงสุด

ด้านล่างนี้คือกราฟของแถวที่แทรกไปยังเวลา CPU ในหน่วยมิลลิวินาที โปรดทราบว่าแกน X มีจุดเริ่มต้นที่แตกต่างกัน:

ด้านบน vs cpu

เราจะเห็นได้ว่าขนาดเซกเมนต์ของกลุ่มแถวเติบโตในอัตราเชิงเส้นและใช้ CPU จำนวนเล็กน้อยจนถึงประมาณ 1 M แถว ณ จุดนั้นขนาดแถวของกลุ่มจะลดลงอย่างมากและการใช้งาน CPU เพิ่มขึ้นอย่างมาก ดูเหมือนว่าเราจะจ่ายซีพียูเป็นจำนวนมากสำหรับการบีบอัดข้อมูลนั้น

เมื่อแทรกน้อยกว่า 1024,000 แถวฉันลงเอยด้วยกลุ่มแถวเปิดใน CCI อย่างไรก็ตามการบังคับให้บีบอัดโดยใช้REORGANIZEหรือREBUILDไม่มีผลกับขนาด ในฐานะที่เป็นกันฉันพบว่ามันน่าสนใจที่เมื่อฉันใช้ตัวแปรสำหรับTOPฉันจบลงด้วยการเปิด rowgroup แต่เมื่อRECOMPILEฉันลงเอยด้วยการปิด rowgroup

ต่อไปฉันทดสอบโดยการเปลี่ยนค่าโมดูลัสในขณะที่ยังคงจำนวนแถวเท่าเดิม นี่คือตัวอย่างของข้อมูลเมื่อทำการแทรกแถว 102400:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    102400     1580          13504          352 
    102400     1590          13584          316 
    102400     1600          13664          317 
    102400     1601          19624          270 
    102400     1602          25568          283 
    102400     1603          31520          286 
    102400     1604          37464          288 
    102400     1605          43408          273 
    102400     1606          49360          269 
    102400     1607          55304          265 
    102400     1608          61256          262 
    102400     1609          67200          255 
    102400     1610          73144          265 
    102400     1620         132616          132 
    102400     1621         138568          100 
    102400     1622         144512           91 
    102400     1623         150464           75 
    102400     1624         156408           60 
    102400     1625         162352           47 
    102400     1626         164712           41 
╚═══════════╩═════════╩═══════════════╩═════════════╝

จนถึงค่า mod ที่ 1600 ขนาดเซ็กเมนต์ rowgroup จะเพิ่มขึ้นเป็นเส้นตรง 80 ไบต์สำหรับค่าที่ไม่ซ้ำกัน 10 ค่าเพิ่มเติม มันเป็นเรื่องบังเอิญที่น่าสนใจที่โดยBIGINTทั่วไปใช้เวลา 8 ไบต์และขนาดเซกเมนต์เพิ่มขึ้น 8 ไบต์สำหรับแต่ละค่าที่ไม่ซ้ำกันเพิ่มเติม ที่ผ่านมาค่า mod ที่ 1600 ขนาดเซ็กเมนต์จะเพิ่มขึ้นอย่างรวดเร็วจนกว่าจะเสถียร

นอกจากนี้ยังเป็นประโยชน์ในการดูข้อมูลเมื่อปล่อยให้ค่าโมดูลัสเหมือนกันและเปลี่ยนจำนวนแถวที่แทรก:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    300000     5000         600656          131 
    305000     5000         610664          124 
    310000     5000         620672          127 
    315000     5000         630680          132 
    320000     5000          40688         2344 
    325000     5000          40696         2577 
    330000     5000          40704         2589 
    335000     5000          40712         2673 
    340000     5000          40728         2715 
    345000     5000          40736         2744 
    350000     5000          40744         2157 
╚═══════════╩═════════╩═══════════════╩═════════════╝

ดูเหมือนว่าเมื่อจำนวนแถวที่แทรก <~ 64 * จำนวนค่าที่ไม่ซ้ำกันที่เราเห็นมีการบีบอัดค่อนข้างต่ำ (2 ไบต์ต่อแถวสำหรับ mod <= 65000) และการใช้ CPU เชิงเส้นต่ำ เมื่อจำนวนแถวที่แทรก> ~ 64 * จำนวนค่าที่ไม่ซ้ำกันเราเห็นการบีบอัดที่ดีขึ้นและการใช้ CPU เชิงเส้นยังคงสูงขึ้น มีการเปลี่ยนแปลงระหว่างสองสถานะซึ่งไม่ใช่เรื่องง่ายสำหรับฉันที่จะสร้างแบบจำลอง แต่สามารถเห็นได้ในกราฟ ดูเหมือนจะไม่เป็นความจริงที่เราจะเห็นการใช้งาน CPU สูงสุดเมื่อทำการแทรก 64 แถวสำหรับแต่ละค่าที่ไม่ซ้ำกัน แต่เราสามารถแทรกแถวได้สูงสุด 1048576 แถวในกลุ่มแถวและเราเห็นการใช้ CPU และการบีบอัดที่สูงขึ้นมากเมื่อมีมากกว่า 64 แถวต่อค่าที่ไม่ซ้ำกัน

ด้านล่างนี้เป็นโครงร่างของการเปลี่ยนแปลงเวลาของซีพียูเมื่อจำนวนแถวที่แทรกและจำนวนแถวที่ไม่ซ้ำกันเปลี่ยนแปลง เราสามารถเห็นรูปแบบที่อธิบายไว้ข้างต้น:

contour cpu

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

ขนาดรูปร่าง

ดูเหมือนว่ามีอัลกอริทึมการบีบอัดที่แตกต่างกันอย่างน้อยสองที่ทำงานที่นี่ ตามที่กล่าวไว้ข้างต้นทำให้รู้สึกว่าเราจะเห็นการใช้งาน CPU สูงสุดเมื่อทำการแทรกแถว 1048576 นอกจากนี้ยังทำให้รู้สึกว่าเราเห็นการใช้งาน CPU มากที่สุด ณ จุดนั้นเมื่อแทรกประมาณ 16,000 แถว 1048576/64 = 16384

ฉันอัปโหลดข้อมูลดิบทั้งหมดของฉันที่นี่ในกรณีที่มีคนต้องการวิเคราะห์

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

วาง 2097152 แถวในตารางเตรียมการ:

DROP TABLE IF EXISTS STG_2097152;
CREATE TABLE dbo.STG_2097152 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_2097152 WITH (TABLOCK)
SELECT TOP (2097152) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

ส่วนแทรกนี้เสร็จสิ้นในเวลาน้อยกว่าหนึ่งวินาทีและมีการบีบอัดที่ไม่ดี:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_2097152 
OPTION (MAXDOP 2);

เราสามารถเห็นผลของเธรดที่ไม่สมดุล:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 OPEN             13540             0         311296 
 COMPRESSED     1048576             0        2095872 
 COMPRESSED     1035036             0        2070784 
╚════════════╩════════════╩══════════════╩═══════════════╝

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

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT FLOOR(0.5 * ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))  % 15999
FROM dbo.STG_2097152
OPTION (MAXDOP 2)

การเลือกตัวเลขคี่สำหรับโมดูลัสเป็นสิ่งสำคัญที่นี่ SQL Server จะสแกนตารางลำดับขั้นในแบบอนุกรมคำนวณหมายเลขแถวจากนั้นใช้การกระจายแบบวนรอบเพื่อวางแถวบนเธรดแบบขนาน นั่นหมายความว่าเราจะจบด้วยหัวข้อที่มีความสมดุลอย่างสมบูรณ์แบบ

ยอดคงเหลือ 1

การแทรกใช้เวลาประมาณ 40 วินาทีซึ่งคล้ายกับการแทรกแบบอนุกรม เราได้รับกลุ่มแถวที่ถูกบีบอัดอย่าง:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 COMPRESSED     1048576             0         128568 
 COMPRESSED     1048576             0         128568 
╚════════════╩════════════╩══════════════╩═══════════════╝

เราสามารถได้ผลลัพธ์เดียวกันโดยการแทรกข้อมูลจากตาราง staging ดั้งเดิม:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT t.ID % 16000 ID
FROM  (
    SELECT TOP (2) ID 
    FROM (SELECT 1 ID UNION ALL SELECT 2 ) r
) s
CROSS JOIN dbo.STG_1048576 t
OPTION (MAXDOP 2, NO_PERFORMANCE_SPOOL);

ที่นี่การกระจายแบบโรบินกลมใช้สำหรับตารางที่ได้รับsดังนั้นการสแกนหนึ่งตารางจะทำในแต่ละเธรดแบบขนาน:

สมดุล 2

โดยสรุปเมื่อแทรกจำนวนเต็มกระจายเท่า ๆ กันคุณสามารถเห็นการบีบอัดสูงมากเมื่อแต่ละจำนวนเต็มไม่ซ้ำกันปรากฏขึ้นมากกว่า 64 ครั้ง อาจเกิดจากอัลกอริธึมการบีบอัดที่แตกต่างกัน อาจมีต้นทุนสูงใน CPU เพื่อให้ได้การบีบอัดนี้ การเปลี่ยนแปลงเล็กน้อยในข้อมูลสามารถนำไปสู่ความแตกต่างอย่างมากในขนาดของเซ็กเมนต์กลุ่มแถวที่ถูกบีบอัด ฉันสงสัยว่าการดูกรณีที่เลวร้ายที่สุด (จากมุมมองของ CPU) จะไม่เป็นเรื่องปกติอย่างน้อยสำหรับชุดข้อมูลนี้ ยิ่งยากที่จะเห็นเมื่อทำการแทรกแบบขนาน


8

ฉันเชื่อว่าสิ่งนี้เกี่ยวข้องกับการเพิ่มประสิทธิภาพภายในของการบีบอัดสำหรับตารางคอลัมน์เดียวและจำนวนเวทย์มนตร์ 64 KB ที่ครอบครองโดยพจนานุกรม

ตัวอย่าง: ถ้าคุณทำงานกับMOD 16600ผลสุดท้ายของขนาดกลุ่มแถวจะเป็น1.683 ล้านบาทในขณะที่ทำงานMOD 17000จะทำให้คุณมีกลุ่มแถวกับขนาดของ2.001 MB

ตอนนี้ให้ดูที่พจนานุกรมที่สร้างขึ้น (คุณสามารถใช้CISL libraryของฉันสำหรับสิ่งนั้นได้คุณจะต้องใช้ฟังก์ชัน cstore_GetDictionaries หรืออีกทางเลือกหนึ่งไปที่ sys.column_store_dictionaries DMV)

(MOD 16600) 61 KB

ป้อนคำอธิบายรูปภาพที่นี่

(MOD 17000) 65 KB

ป้อนคำอธิบายรูปภาพที่นี่

สิ่งที่ตลกถ้าคุณจะเพิ่มคอลัมน์อื่นลงในตารางของคุณและเรียกมันว่า REALID:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, REALID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

โหลดข้อมูลสำหรับ MOD 16600:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16600, ID
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

เวลานี้การดำเนินการจะเร็วเนื่องจากเครื่องมือเพิ่มประสิทธิภาพจะตัดสินใจไม่ทำงานหนักเกินไปและบีบอัดมันจนเกินไป:

select column_id, segment_id, cast(sum(seg.on_disk_size) / 1024. / 1024 as Decimal(8,3) ) as SizeInMB
    from sys.column_store_segments seg
        inner join sys.partitions part
            on seg.hobt_id = part.hobt_id 
    where object_id = object_id('dbo.CCI_BIGINT')
    group by column_id, segment_id;

แม้ว่าจะมีความแตกต่างเล็กน้อยระหว่างขนาดกลุ่มแถว แต่ก็จะเล็กน้อย (2.000 (MOD 16600) เทียบกับ 2.001 (MOD 17000))

สำหรับสถานการณ์นี้พจนานุกรมสำหรับ MOD 16000 จะใหญ่กว่าสำหรับสถานการณ์แรกที่มี 1 คอลัมน์ (0.63 เทียบกับ 0.61)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.