CƠ SỞ LÝ THUYẾT
Giới thiệu về tính toán song song
1.1.1 Thế nào là lập trình, tính toán song song?
Tính toán song song là phương pháp cho phép thực hiện nhiều phép tính cùng lúc, dựa trên nguyên tắc phân chia các vấn đề lớn thành những vấn đề nhỏ hơn để giải quyết một cách hiệu quả.
Tính toán song song bao gồm nhiều hình thức như song song dữ liệu, song song cấp lệnh và song song tác vụ Trong nhiều năm qua, nó đã được áp dụng chủ yếu trong lĩnh vực tính toán hiệu năng cao Hiện nay, tính toán song song đã trở thành mô hình thống trị trong kiến trúc máy tính, đặc biệt dưới dạng bộ xử lý đa nhân.
Phần mềm thường được thiết kế để thực hiện tính toán tuần tự trên máy tính đơn với một bộ xử lý trung tâm Một bài toán sẽ được phân chia thành các câu lệnh rời rạc, và những câu lệnh này được thực hiện theo thứ tự, với chỉ một câu lệnh được thực hiện tại mỗi thời điểm.
Tính toán song song là việc sử dụng đồng thời nhiều tài nguyên máy tính để giải quyết các bài toán phức tạp Để thực hiện điều này, một bài toán lớn được chia thành nhiều phần nhỏ có thể xử lý đồng thời, mỗi phần lại được chia nhỏ hơn thành các câu lệnh Các câu lệnh này sau đó được thực thi trên các CPU khác nhau, giúp tăng tốc độ xử lý và hiệu quả tính toán.
Hình 1 2 Tính toán song song
Vấn đề của tính toán song song thường được thể hiện qua các đặc điểm như khả năng:
- Chia thành các phần riêng biệt các công việc để có thể giải quyết cùng một lúc.
- Thực thi nhiều cầu lệnh chương trình tại nhiều thời điểm.
- Giải quyết bài toán trong thời gian ít hơn và nhiều tài nguyên máy tính hơn là thực thi trê một tài nguyên duy nhất.
1.1.2 Tại sao cần tính toán song song?
Với sự phát triển của công nghệ thông tin, bộ xử lý đa nhân đang dần thay thế bộ xử lý đơn lõi Tuy nhiên, lập trình truyền thống với cách xử lý tuần tự không tận dụng được tối đa hiệu năng của bộ vi xử lý đa nhân Lập trình và tính toán song song ra đời nhằm đáp ứng nhu cầu tối ưu hóa công năng của bộ đa xử lý.
Trong bối cảnh hiện nay, nhiều bài toán phức tạp liên quan đến dữ liệu lớn đòi hỏi thời gian xử lý ngắn và độ chính xác cao Các ví dụ tiêu biểu bao gồm xử lý ảnh, xử lý tín hiệu, dự báo thời tiết, mô phỏng giao thông, và mô phỏng chuyển động của phân tử, nguyên tử, cũng như khai thác cơ sở dữ liệu Tuy nhiên, với bộ xử lý đơn lõi, việc thực hiện và đạt được kết quả mong muốn cho những bài toán này là rất khó khăn.
Lập trình và tính toán song song giúp nâng cao hiệu suất xử lý, đồng thời rút ngắn thời gian tính toán cho người dùng.
Việc tính toán song song có thể:
Tận dụng tài nguyên tính toán từ mạng diện rộng hoặc internet là giải pháp hiệu quả khi tài nguyên cục bộ bị hạn chế.
- Tiết kiệm chi phí – sử dụng nhiều tài nguyên máy tính “rẻ” thay vì đầu tư một hệ thống siêu máy tính đắt đỏ.
Để khắc phục những hạn chế về bộ nhớ của các máy tính đơn, việc sử dụng bộ nhớ từ nhiều máy tính sẽ giúp vượt qua những trở ngại này, đặc biệt đối với các bài toán lớn.
1.1.3 Tính toán song song để làm gì?
Phát huy công năng, hiệu năng của bộ xử lý đa nhân, đa lõi.
Giải quyết các bài toán phức tạp mà bộ xử lý đơn lõi không thể xử lý, đồng thời nâng cao hiệu suất tính toán và giảm thiểu thời gian thực hiện.
Tính toán song song là sự phát triển của tính toán tuần tự, nhằm mô phỏng các trạng thái phức tạp trong thế giới tự nhiên, nơi các sự kiện liên quan diễn ra đồng thời trong cùng một chuỗi sự kiện.
- Quỹ đạo hành tính và thiên hà
- Các mô hình thời tiết cà đại dương.
- Dây chuyền lắp ráp ô tô.
Tính toán song song, thường được xem là "tính toán hiệu năng cao", đóng vai trò quan trọng trong việc mô phỏng các hệ thống phức tạp nhằm giải quyết những bài toán lớn.
- Dự báo thời tiết và khí hậu.
- Các phản ứng hóa học và hạt nhân.
- Các bài toán sinh học và gen người.
- Các hoạt động địa chất.
- Các thiết bị cơ khí phức tạp.
1.1.4 So sánh tính toán tuần tự và lập trình tính toán song song.
Lập trình tính toán tuần tự Lập trình tính toán song song
- Chương trình ứng dụng chạy trên bộ xử lý đơn (single processor).
- Các chỉ thị lệnh được bộ xử lý (CPU) thực hiện một cách lần lượt, tuần tự.
- Mỗi chỉ thị lệnh chỉ thực thiện trên duy nhất một thành phần dữ liệu.
- Lập trình viên chỉ cần đảm bảo viết đúng mã lệnh theo giải thuật chương trình là chương trình có thể dịch, chạy và cho ra kết quả.
- Thường được áp dụng đối với các bài toán có dữ liệu nhỏ, độ
- Chương trình ứng dụng chạy trên hai hoặc nhiều bộ xử lý.
- Các chỉ thị lệnh được các bộ vi xử lý thực hiện một cách song song, đồng thời.
- Mỗi chỉ thị lệnh có thể thao tác trên hai hoặc nhiều thành phần dữ liệu khác nhau.
Lập trình viên không chỉ cần viết mã lệnh chính xác theo thuật toán mà còn phải xác định rõ các đoạn mã nào trong chương trình được thực hiện song song và đồng thời.
Phương pháp này thường được áp dụng cho các bài toán với dữ liệu lớn và phức tạp, nơi thời gian xử lý là yếu tố quan trọng Nó giúp giải quyết các vấn đề có độ phức tạp cao trong khoảng thời gian ngắn, tối ưu hóa hiệu suất và kết quả.
Một số vấn đề liên quan đến lập trình và tính toán song song
1.2.1 Định luật Amdal’s Định luật được Amdahl‟s phát biểu vào năm 1967 nhằm đánh giá hiệu năng của việc tính toán song song. Định luật được phát biểu như sau: Hiệu năng tính toán của chương trình được định nghĩa là phân số của đoạn mã mà được thực thi song song.
- Nếu không có đoạn mã được thực thi song song, P = 0 speedup = 1 (Không cải thiện được tốc độ).
- Nếu tất cả các đoạn mã được thực thi song song, P = 1 speedup tăng lên đến vô cùng.
- Nếu 50% đoạn mã được thực thi song song, speedup đạt giá trị max. Công thức nêu lên mối quan hệ giữa hiệu năng tính toán với bộ xử lý.
Trong quá trình xử lý song song, dữ liệu thường được phân phối trên các bộ nhớ cục bộ của các bộ vi xử lý, đòi hỏi khối lượng công việc cần được phân phối hợp lý Tuy nhiên, thực tế cho thấy không phải lúc nào việc phân phối này cũng diễn ra hiệu quả, dẫn đến tình trạng một số bộ xử lý bị quá tải trong khi những bộ xử lý khác không khai thác hết khả năng tính toán Giải pháp cho vấn đề này là áp dụng cân bằng tải động để phân phối công việc một cách hợp lý giữa các bộ xử lý.
Thông thường khi phân phối xong công việc cho các bộ xử lý, quá trình cân bằng tải động sẽ thực hiện theo các bước sau đây:
- Giám sát hiệu năng của mỗi bộ xử lý.
- Trao đổi thông tin trạng thái giữa các bộ xử lý.
- Tính toán và ra quyết định phân phối lại công việc.
Để thực hiện chuyển đổi dữ liệu cho các bộ xử lý, có nhiều thuật toán được phát triển Những thuật toán này thường được phân loại thành các lớp khác nhau.
Cân bằng tải tập trung là phương pháp đưa ra quyết định tổng thể trong việc phân phối công việc cho các bộ xử lý, sử dụng thông tin hệ thống toàn cục để theo dõi trạng thái của các bộ xử lý Điều này cho phép thuật toán dễ dàng phân phối lại công việc, nhưng khối lượng công việc tăng theo tỷ lệ với số lượng bộ xử lý, đòi hỏi bộ nhớ lớn trên từng bộ xử lý để lưu trữ thông tin Do đó, các thuật toán trong lớp này ít được áp dụng.
Cân bằng tải phân tán hoàn toàn là chiến lược trong đó mỗi bộ xử lý giữ một bản sao thông tin trạng thái của hệ thống Các bộ xử lý trao đổi thông tin trạng thái với nhau để điều chỉnh cục bộ việc phân chia công việc Tuy nhiên, do chỉ có thông tin cục bộ, hiệu quả của phương pháp này không cao bằng các thuật toán cân bằng tải tập trung.
Cân bằng tải phân tán một nửa là phương pháp mà các thuật toán chia các bộ xử lý thành từng miền riêng biệt Mỗi miền này áp dụng thuật toán cân bằng tải tập trung để phân chia khối lượng công việc hiệu quả cho các bộ xử lý, giúp tối ưu hóa hiệu suất và tăng cường khả năng xử lý.
Sự bế tắc xảy ra khi nhiều bộ xử lý cùng sử dụng chung một tài nguyên hệ thống mà không có sự kiểm soát hiệu quả Hiện tượng này thường xuất hiện trong các hệ điều hành đa nhiệm, hệ thống đa bộ xử lý và đa máy tính Đặc biệt, trong các hệ thống đa máy tính, bế tắc vùng đệm là một tình huống phổ biến, xảy ra khi một tiến trình chờ nhận thông điệp mà có thể không bao giờ đến được do vùng đệm của hệ thống đã đầy.
Các điều kiện gây lên sự bế tắc:
- Sự loại trừ lẫn nhau: Mỗi tiến trình có sự độc quyền khi sử dụng tài nguyên riêng của nó.
Trong hệ thống quản lý tiến trình, không có sự ưu tiên giữa các tiến trình, nghĩa là mỗi tiến trình sẽ không giải phóng tài nguyên mà nó đang chiếm giữ cho đến khi không còn cần thiết sử dụng chúng nữa Điều này có thể dẫn đến tình trạng tắc nghẽn tài nguyên nếu không được quản lý hợp lý.
- Sự chờ đợi tài nguyên: Mỗi tiến trình đang chiếm giữ tài nguyên trong khi lại đợi tiến trình khác giải phóng tài nguyên cho chúng.
- Sự chờ đợi giữa các tiến trình: Tiến trình đợi tài nguyên mà tiến trình kế tiếp đang chiếm giữ mà tài nguyên đó không được giải phóng.
Một số cách khắc phục:
Để quản lý tình trạng bế tắc, có ba phương pháp chính Đầu tiên, chúng ta có thể dò tìm và khôi phục khi bế tắc xảy ra Thứ hai, việc phân phối tài nguyên hợp lý dựa trên thông tin yêu cầu của các tiến trình giúp tránh tình trạng bế tắc Cuối cùng, ngăn chặn sự đồng thời xảy ra của ba điều kiện cuối cùng trong quá trình phát sinh bế tắc là cách hiệu quả để duy trì sự ổn định.
Tổng quan về các mô hình tính toán song song
Có nhiều mô hình lập trình song song thường được sử dụng như:
- Mô hình chia sẻ bộ nhớ.
- Mô hình gửi thông điệp.
- Mô hình song song dữ liệu.
- Mô hình lai giữa các mô hình.
Các mô hình lập trình song song là những trừu tượng hóa quan trọng trong kiến trúc phần cứng và phần mềm Chúng không gắn liền với một kiểu kiến trúc máy tính hay bộ nhớ cụ thể nào Thực tế, bất kỳ mô hình nào cũng có thể được áp dụng cho mọi kiến trúc phần cứng, cho thấy tính linh hoạt và khả năng thích ứng cao của chúng.
1.3.1 Mô hình chia sẻ bộ nhớ
Trong mô hình lập trình chia sẻ bộ nhớ, các tác vụ có thể truy cập không đồng bộ vào không gian địa chỉ chung Để quản lý quyền truy cập đến vùng nhớ chia sẻ, các kỹ thuật như khóa (lock) và semaphore thường được áp dụng.
Một lợi thế của mô hình này cho lập trình viên là không tồn tại khái niệm quyền sở hữu dữ liệu, điều này giúp loại bỏ sự cần thiết phải xác định rõ ràng cách thức giao tiếp dữ liệu giữa các tác vụ Nhờ vậy, quá trình phát triển chương trình trở nên đơn giản hơn.
Một nhược điểm quan trong về hiệu suất là nó trở nên khó khăn trong việc hiểu và quản lý dữ liệu cục bộ.
1.3.2 Mô hình gửi thông điệp
Hình 1 3 Mô hình gửi thông điệp
Mô hình truyền thông điệp được định nghĩa là:
- Đặt quá trình xử lý sử dụng một bộ nhớ cục bộ.
- Các bộ xử lý trao đổi với nhau thông qua việc gửi và nhận các thông điệp.
- Sự di chuyển dữ liệu yêu cầu sự kết hợp thao tác thực hiện của mỗi quá trình xử lý (truyền nhận thông điệp một cách nhịp nhàng).
Lập trình với mô hình truyền thông điệp kết nối các thư viện để quản lý dữ liệu trao đổi giữa các bộ xử lý, với nhiều thư viện có sẵn trong một số ngôn ngữ lập trình.
Mô hình gửi thông điệp (Message Passing) có một số đặc điểm như sau:
Một tập hợp các tác vụ sử dụng bộ nhớ cục bộ riêng để thực hiện các phép tính Nhiều tác vụ này có thể được triển khai trên cùng một máy tính hoặc phân phối qua nhiều máy tính khác nhau.
- Các tác vụ trao đổi dữ liệu thông qua truyền thông gửi và nhận thông điệp.
Truyền dữ liệu yêu cầu sự phối hợp chặt chẽ giữa các xử lý, trong đó thao tác gửi cần phải tương ứng với thao tác nhận để đảm bảo tính chính xác và hiệu quả trong quá trình truyền.
Trong lập trình, việc thực thi mô hình gửi thông điệp thường bao gồm một thư viện các chương trình được nhúng vào mã nguồn Các lập trình viên cần xác định tất cả các xử lý song song để đảm bảo hiệu suất và tính ổn định của hệ thống.
Hiện nay, MPI được công nhận là một chuẩn công nghiệp và là chuẩn "de facto" cho việc kết nối các nút trong chương trình song song trên hệ thống bộ nhớ phân tán Trong kiến trúc bộ nhớ chia sẻ, MPI thường không sử dụng giao tiếp qua mạng giữa các tác vụ, mà thay vào đó, tận dụng bộ nhớ chia sẻ để tối ưu hiệu suất.
Tập MPI thực thi bao gồm thư viện các thủ tục sao cho có thể gọi được từ các chương trình Fortran, C, C++ hay Ada.
1.3.3 Mô hình song song dữ liệu
Mô hình song song dữ liệu (Data parallel) được định nghĩa là:
- Mỗi quá trình xử lý công việc được thực hiện trên một thành phần của cấu trúc dữ liệu.
- Thường áp dụng với chương trình nhiều dữ liệu Single Program Multiple Data (SPMD)
- Dữ liệu của chương trình sẽ được chia cho các bộ xử lý.
- Người lập trình không thấy được quá trình trao đổi dữ liệu.
- Thường được xây dựng theo kiểu “on top of ” một kiểu của thư viện Message Passing.
Khi lập trình với mô hình song song, lập trình viên cần xây dựng chương trình dựa trên cấu trúc dữ liệu song song và sử dụng trình biên dịch dữ liệu song song (Data parallel Compiler) để biên dịch chương trình.
Chương trình dịch sẽ dịch chương trình thành mã chuẩn và gọi tới thư viện Message Passing để chia dữ liệu cho tất cả quá trình xử lý.
Hình 1 4 Mô hình song song dữ liệu
Mô hình song song dữ liệu thể hiện qua các đặc điểm sau:
Hầu hết các công việc song song đều tập trung vào việc thực hiện các thao tác trên một bộ dữ liệu Bộ dữ liệu này thường được tổ chức theo một cấu trúc chung như mảng hoặc khối, giúp tối ưu hóa quá trình xử lý và nâng cao hiệu suất làm việc.
Một tập hợp các tác vụ hoạt động trên cùng một cấu trúc dữ liệu, nhưng mỗi tác vụ lại xử lý các phần khác nhau của cấu trúc đó.
- Các tác vụ thực hiện cùng hành động trên phân vùng công việc của chúng.
Trong kiến trúc chia sẻ bộ nhớ, tất cả các tác vụ truy cập cấu trúc dữ liệu qua bộ nhớ toàn cục, trong khi kiến trúc phân tán cho phép cấu trúc dữ liệu được chia nhỏ và lưu trữ trong bộ nhớ cục bộ của từng tác vụ Lập trình với mô hình song song dữ liệu thường được thực hiện thông qua việc viết chương trình với các cấu trúc dữ liệu song song, có thể sử dụng thư viện thủ tục song song hoặc được nhận diện bởi các chỉ thị biên dịch của chương trình biên dịch song song dữ liệu.
Thực thi bộ nhớ phân tán theo mô hình này thường yêu cầu trình biên dịch chuyển đổi chương trình thành mã chuẩn, kết hợp với việc sử dụng thư viện truyền tin (thường là MPI) để phân phối dữ liệu cho tất cả các tiến trình Tất cả các thông điệp được thực hiện “trong suốt” từ góc độ của lập trình viên.
Trong lập trình song song theo mô hình luồng, một xử lý đơn có thể trở thành đa xử lý thông qua việc thực thi đồng thời Luồng (Thread) là khái niệm mô tả một chương trình chính có nhiều chương trình và thủ tục con, cho phép thực hiện song song trong quá trình chạy chương trình chính.
- Khi chương trình chính thực thi, nó thực hiện mốt số bước tuần tự và tạo ra các Thread mà sau đó được thực hiện một cách đồng thời.
- Mỗi Thread có dữ liệu cục bộ nhưng chúng dùng chung tài nguyên của chương trình chính.
Mỗi Thread trong một chương trình hoạt động như một thủ tục con, cho phép thực thi song song với các Thread khác trong cùng một khoảng thời gian Điều này giúp tối ưu hóa hiệu suất và tăng cường khả năng xử lý đa nhiệm của hệ thống.
- Các Thread trao đổi với nhau thông qua bộ nhớ toàn cục bằng cách cập nhật địa chỉ bộ nhớ toàn cục.
Hình 1 5 Mô hình luồng Đơn giản nhất để mô tả mô hình luồng, ta phân tích trên ví dụ với một số thủ tục trong chương trinh:
Giới thiệu về OpenMP
OpenMP (Open Multi-Processing) là một giao diện lập trình ứng dụng (API) cho phép điều khiển các luồng dựa trên cấu trúc chia sẻ bộ nhớ chung Các thành phần chính của OpenMP bao gồm các chỉ thị, thư viện và biến môi trường, giúp lập trình viên dễ dàng phát triển các ứng dụng đa luồng hiệu quả.
- Các chỉ thị biên dịch (Compiler Directive).
- Thư viện runtime (Runtime Library Rountines).
Các biến môi trường (Environment Variables) trong OpenMP được xác định dựa trên các yếu tố phần cứng và phần mềm OpenMP là một thư viện giúp lập trình viên dễ dàng và linh hoạt phát triển các chương trình song song trên máy tính cá nhân hỗ trợ nhiều bộ xử lý.
Chương trình OpenMP được thực thi bằng cách tuân theo các chỉ thị song song.
OpenMP là một bộ công cụ lập trình cho phép lập trình song song, trong đó các lệnh không được trình biên dịch hỗ trợ sẽ bị coi là nhận xét và bị bỏ qua Điều này cho phép lập trình viên viết chương trình OpenMP giống như một chương trình tuần tự Trong OpenMP, yếu tố chính không phải là tiến trình mà là luồng, tạo điều kiện cho việc xử lý song song hiệu quả hơn.
1.4.2 Mô hình lập trình song song trong OpenMP
Mô hình được dùng để lập trinh song song trong OpenMP là mô hình Fork – Join.
Mô hình Fork - Join cho phép các chương trình chạy tuần tự bởi luồng chủ cho đến khi gặp vùng song song Tại điểm này, luồng chủ sẽ "fork" để tạo ra các luồng thực hiện song song, giúp các đoạn mã song song được thực thi đồng thời, tối ưu hóa hiệu suất xử lý.
Khi các luồng thực thi mã trong vùng song song hoàn tất, chúng sẽ được đồng bộ hóa, sau đó công việc sẽ tiếp tục được thực hiện bởi luồng chính.
1.4.3 Các chỉ thị biên dịch
Chỉ thị biên dịch là yếu tố thiết yếu cho mỗi ứng dụng song song, giúp trình biên dịch nhận diện khối mã thực hiện song song một cách chính xác.
Khuôn dạng của chỉ thị:
Chỉ thị trong OpenMP được cho dưới dạng sau:
#pragma omp directive- name [clause…] newline
#pragma omp là yêu cầu bắt buộc cho tất cả các chỉ thị trong OpenMP, thông báo cho chương trình về sự bắt đầu của khối mã song song.
- Directive-name: Tên của chỉ thị, tên của chỉ thị pahỉ xuất hiện sau
#pragma và đứng trước bất kỳ mệnh đề nào.
- Clause: Các chỉ thị này không bắt buộc trong chỉ thị, các chỉ thị này sẽ đưa ra phạm vi hoạt động của các biến đối với các thread.
- newline: Yêu cầu bắt bộc đối với mỗi cấu trúc chỉ thị Nó là tập mã lệnh nằm trong khối cấu trúc được bao bọc bởi chỉ thị.
#pragma omp parallel shared (a, b) private(i) { .
// các khối mã được thực hiện song song
Phạm vi của chỉ thị:
Phạm vi tĩnh (static extent).
Phạm vi tĩnh của chỉ thị được xác định từ thời điểm khai báo cho đến khi gặp dấu kết thúc trong vùng song song Điều này liên quan đến chỉ thị đơn độc (ophaned directive).
Chỉ thị đơn độc là loại chỉ thị xuất hiện độc lập với các chỉ thị khác, thường được tìm thấy trong các hàm con của chương trình Nó có vai trò quan trọng trong việc mở rộng đoạn mã thực hiện song song, giúp nâng cao hiệu suất xử lý Phạm vi động (dynamic extent) của chỉ thị này cũng đóng góp vào khả năng quản lý tài nguyên hiệu quả trong lập trình.
- Phạm vi động của chỉ thị bao gồm phạm vi tĩnh của chỉ thị và phạm vi đơn độc của chỉ thị.
Hình 1 7 Phạm vi chỉ thị
Cấu trúc vùng song song:
Một vùng song song là một khối mã mà được thực thi bởi nhiều threads Chúng có khuôn dạng như sau:
#pragma omp parallel [clause .] newline if (scalar_expression) private (list) shared (list) default (shared | none) firstprivate (list) reduction (operator: list) copyin (list) num_threads (integer-expression) structured_block
Khi một luồng gặp chỉ thị PARALLEL, nó sẽ tạo ra một tập hợp các luồng, trong đó luồng đầu tiên được gọi là luồng chủ Luồng chủ có chỉ số là 0, trong khi các luồng tiếp theo sẽ có chỉ số là i-1, với i là chỉ số thứ tự của luồng.
Khi khởi tạo một vùng song song, mã nguồn sẽ được sao chép thành nhiều bản để các luồng thực hiện song song Ở cuối đoạn mã song song, có một điểm đồng bộ mặc định để tất cả các luồng đồng bộ hóa, sau đó mã sẽ được thực hiện tuần tự bởi luồng chính Để xác định số lượng luồng thực thi trong vùng song song, OpenMP cung cấp hàm omp_get_num_threads() để trả về tổng số luồng và omp_get_thread_num() để lấy chỉ số của luồng hiện tại.
TỔNG QUÁT BÀI TOÁN
Phát biết bài bài toán
Giải bài toán tìm nghiệm của 1 hệ phương trình tuyến tính N ẩn.
Với N là số nguyên dương nhập từ bàn phím.
Giải pháp thực hiện bài toán
Sử dụng phương pháp Gauss – Seidel (phương pháp tự sửa sai)
Biến đổi hệ phương trình về dạng: ⃗ x = B ⃗ x + ⃗ g trong đó: ⃗ x = ( x 1 , x 2 , x 3 , …, x n)
2.2.2 Thiết kế thuật toán tuần tự Độ phức tạp của thuật toán: O(n 4 )
2.2.3 Thiết kế thuật toán song song
#pragma omp parallel for num_threads(8) schedule(static, n) reduction(+:dxi)
X[i] = dxi / A[i*n+i] sum += ((dx[i] >= 0.0) ? dx[i] : -dx[i]) if (sum