Với mỗi bài toán, hệ thống sẽ ghi vào một tệp tin có tên chính là sessionID của bài toán ấy với phần mở rộng là smt. Và lời giải của bài toán đó cũng sẽ được lưu trữ vào tệp tin có tên là sessionID.
Hiện tại, trên thế giới có nhiều các bộ giải bài toán SMT, tuy nhiên, trong khuôn khổ luận văn này, hệ thống chỉ cài đặt và thử nghiệm trên hai bộ giải là yices của SRI international và Z3 của Microsoft. Mỗi bộ giải sẽ được cài đặt trên một máy trạm khác nhau cùng với hệ thống được xây dựng như đã trình bày ở trên. Do các bộ giải là các chương trình chạy độc lập, nên hệ thống cần có lời gọi thực thi tới chúng. Tuy ngôn ngữ lập trình Java có hỗ trợ việc gọi một lệnh của hệ điều hành, tuy nhiên để có thể kết xuất được kết quả ra một tệp tin, hệ thống sử dụng quá trình chuyển hướng kết xuất của chính hệ điều hành được sử dụng.
Do điệu kiện không cho phép, hiện tại hệ thống cài đặt trên máy trạm mới chỉ được triển khai và kiểm thử trên các máy sử dụng hề điều hành windows và hệ hiều hành có nhân Linux. Và để sử dụng được chuyển hướng kết xuất trên mỗi hệ điều hành khác nhau, hệ thống đã xây dựng một tệp tin thực thi để hỗ trợ hệ thống gọi các bộ giải SMT. Nội dung tệp tin hỗ trợ đó như sau:
- Đối với tệp tin .bat được cài đặt trên hệ điều hành windows
@echo off
- Đối với tệp tin thực thi (tệp tin được thiết lập thuộc tính thực thi) được cài đặt trên hệ điều hành Linux:
[path]/z3.exe /m /t:$2 /smt [path]/$1.smt > [path]/$1.txt echo done!
- Ở đây, [path] là đường dẫn chỉ đến tệp tin (thư mục) của chương trình bộ giải SMT. %1 sẽ được nhận vào là sesstionID của bài toán. %2 sẽ là tham số thời gian ngắt của chương trình (được tính bằng giây).
- Với bộ giải yices, ta sử dụng tham số -e để đưa ra kết quả thỏa mãn nếu bài toán đó khả thi. Tham số –tm để xác định trễ thời gian ngắt timeout, và tham số -smt để xác định đầu vào của bộ giải là một tệp tin theo chuẩn SMT-LIB.
- Với bộ giải z3, tương tự ta cũng có các tham số /m tương đương với –e, /t: tương đương với –tm và /smt tương đương với –smt của bộ giải yices.
- Cả hai bộ giải yices và z3 đều hỗ trợ cài đặt trên cả windows và Linux, vì vậy, tùy vào điệu kiện của máy trạm, tệp tin thực thi sẽ được sửa cho phù hợp với hệ điều hành cài đặt (không nhất thiết cài đặt yices trên hệ điều hành windows hoặc z3 trên linux).
5.3.4. Các lớp của hệ thống máy trạm
5.3.4.1. Biểu đồ lớp của hệ thống
Hình 5.1: Biểu đồ lớp của hệ thống máy trạm
5.3.4.2. Lớp config
Lớp config chứa những quy định về địa chỉ, cổng và các thẻ giao tiếp với máy chủ của máy trạm. Ngoài ra, lớp này còn quy định tên và đường dẫn của bộ giải được cài đặt trên máy trạm. Việc xây dựng lớp config sẽ khiến cho việc quản lý cấu hình cho hệ thống dễ dàng hơn.
5.3.4.3. Lớp sessionID
Để xác định được đúng dữ liệu của mỗi bài toán, tránh nhầm lẫn khi nhận nhiều dòng dữ liệu từ nhiều bài toán cùng một lúc, và để gửi đúng lời giải cho bài toán đưa ra, ngoài việc quy định mã sessionID cho mỗi bài toán, cần phải tổ chức dữ liệu hợp lý. Đối với kết quả của một bài toán được gửi đến, ta chỉ cần đưa kết quả đó vào một
tệp tin với trên tệp tin là sessionID của bài toán đó, rồi gửi lại máy chủ session ID kèm nội dung tệp tin đó. Còn đối với việc đọc dữ liệu đầu vào, hệ thống xây dựng một lớp sessionID và có một mảng đối tượng của lớp dữ liệu này.
Lớp session ID sẽ bao gồm các thuộc tính sau: - sessionID: là thuộc tính lưu lại sessionID
- content: Thuộc tính lưu lại nội dung của bài toán tương ứng với sessionID được gửi lên.
- Timeout: thời gian ngắt của bài toán được gửi lên
- runSolver: tiến trình gọi một bộ giải để giải bài toán được gửi lên.
Mỗi đối tượng của lớp sessionID sẽ tương ứng với một bài toán được gửi lên. Nội dung của bài toán sẽ được lưu trong thuộc tính content của lớp. Mỗi tiến trình xử lý sẽ được khai báo bởi thuộc tính runSolver của lớp. Việc đưa tiến trình xử lý gọi bộ giải để giải bài toán vào thành thuộc tính của lớp sessionID nhằm giải quyết vấn đề ngắt tiến trình khi có yêu cầu từ phía máy chủ. Khi máy chủ nhận được kết quả của cùng bài toán từ máy trạm có cài đặt bộ giải khác và gửi lên tín hiệu ngắt cho máy trạm đang còn thực thi tiến trình giải bài toán ấy, thì việc ngắt tiến trình tương ứng với sessionID mà máy chủ gửi lên sẽ được gọi.
5.3.4.4. Lớp Solver
Lớp này được xây dựng để thực hiện nhiệm vụ mở kết nối và có lời chào hỏi trước khi thực hiện các thao tác khác sau đó.
5.3.4.5. Lớp ReadThread
Lớp này được xây dựng để đáp ứng việc đọc dữ liệu từ máy chủ gửi lên của máy trạm. Sau khi kết nối được giữa máy chủ và máy trạm, hệ thống trên máy trạm luôn đảm bảo việc lắng nghe các tín hiệu từ máy chủ. Để đảm bảo rằng, luôn có thể nhận được nhiều dòng dữ liệu từ nhiều các bài toán khác nhau cùng một lúc.
Vấn đề kiểm soát bộ nhớ (mảng các đối tượng sessionID) sẽ được đánh dấu bằng việc bài toán đã được giải xong hay chưa. Khi một sessionID mới được nhận về, một phần tử của mảng các đối tượng sessionID mà có trạng thái rảnh sẽ được khởi tạo và lưu lại. khi đó, trạng thái tương ứng của sessionID được bật là bận. Do việc giải phóng bộ nhớ phụ thuộc vào việc bài toán được giải xong hay chưa, vì vậy trạng thái của sessionID được khai báo trong lớp các đối tượng tiến trình ghi dữ liệu. Tức là tương ứng với mảng các đối tượng của lớp sesstionID, sẽ có một mảng các đối tượng ghi dữ liệu kết quả trả về được khởi tạo.
while (true){
_return = in.readLine(); //Đọc dữ liệu gửi lên từ phía máy chủ if (_return.indexOf(config._tag_file)>=0){
openFile(_return); // Tạo mới một tệp tin lưu trữ bài toán mới }
else if(_return.indexOf(config._closetag_file)>=0){ } else if(_return.indexOf(config._tag_timeout)>=0){
sec[k].setTimeout(_return.substring(p+1));//đặt trễ thời gian cho bài toán
closeFile(_return); // }
else if(_return.indexOf(config._tag_destroy)>=0){ k = pos(_return); // xác định bài toán cần ngắt interruptSolver(k); // Ngắt tiến trình xử lý bài toán }
else {
k = pos(_return);
String content = _return.substring(p+1);
sec[k].content += content + "\n"; //lưu lại nội dung bài toán }
Ngay khi tiếp nhận một bài toán mới (nhận được thẻ mở tệp tin), đối tượng của lớp ghi dữ liệu tương ứng sẽ được đặt thuộc tính là bận. Việc quản lý thiết lập thuộc tính bận hay rảnh này sẽ giúp cho hệ thống giới hạn được số lượng phần tử mảng luồng đọc ghi dữ liệu. Trong trường hợp không quản lý trạng thái rảnh hay bận của các đối tượng này, mỗi khi nhận được bài toán mới, hệ thống sẽ sinh ra một phần tử của mảng các đối tượng ghi dữ liệu. Như vậy, các đối tượng ghi xong và được giải phóng sẽ không được sử dụng lại, gây ra lãng phí tài nguyên.
Việc gọi tiến trình thực thi các bộ giải sẽ được thực hiện khi việc nhận dữ liệu về bài toán được hoàn tất. Sau khi nhận được tham số trễ thời gian, hệ thống sẽ tiến hành ghi dữ liệu ra tệp tin và gọi lệnh gọi bộ giải để giải quyết bài toán. Và khi đó, tiến trình này sẽ được truyền cho đối tượng của lớp ghi dữ liệu để nhận và trả về kết quả.
String command = config._command + " " + sec[k].sectionID + " " + sec[k].getTimeout();
sec[k].runSolver = Runtime.getRuntime().exec(command); wt[k].setProc(sec[k].runSolver);
public ReadThread(Socket sk){ try {
for (int i = 0;i<config.Max_connection;i++) {
sec[i] = new section(); //khởi tạo mảng các đối tượng sessionID wt[i] = new WriteThread(sk); //Khởi tạo mảng các đối tượng ghi dữ liệu }
}
catch (IOException e){ e.printStackTrace(); }
5.3.4.6. Lớp WriteThread
Việc chờ kết quả để gửi trả về phía máy chủ cần được xây dựng thành một luồng làm việc riêng biệt bởi đi kèm với việc gửi trả về kết quả, là việc trả về không gian bộ nhớ cho thành phần đối tượng thuộc lớp sessionID. Ngoài ra, luồng này còn luôn sẵn sàng để ngắt tiến trình đang thực hiện nếu có yêu cầu từ phía máy chủ gửi lên. Ngoài ra, tính cần thiết phải đưa thành một luồng làm việc riêng biệt còn thể hiện ở việc đáp ứng yêu cầu luôn lắng nghe dữ liệu gửi lên từ phía máy chủ.
Đối với việc gọi và thực thi bộ giải, sau khi dữ liệu được ghi vào tệp tin hoàn tất, một tiến trình (thành phần của lớp sessionID) sẽ gọi công cụ giải được cài đặt trên máy trạm. Khi nhận được tín hiệu “done!” xuất hiện từ luồng dữ liệu ra của tiến trình, bài toán đã được giải xong và quá trình gửi dữ liệu về máy chủ được bắt đầu. Cùng với đó, bộ nhớ chứa dữ liệu của bài toán được thực thi xong sẽ được giải phóng.
Việc ngắt một tiến trình khi nhận được yêu cầu ngắt từ phía máy chủ cũng được đẩy sang phương thức của lớp này. Bởi lẽ, việc ngắt tiến trình còn liên quan đến việc thiết lập trạng thái rảnh cho đối tượng.
5.4.Tổng kết
Mỗi hệ thống con của hệ thống giải bài toán SMT hiệu năng cao mang vai trò riêng và đều rất quan trọng với hệ thống. Việc xây dựng tốt hệ thống máy khách sẽ
public void interruptSolver(){
this.proc.destroy(); //ngắt tiến trình đang xử lý this.active = false; // trả về trạng thái rảnh }
if (line.equals("done!")){
sendFile(); // gửi tệp tin kết quả
this.active = false; // thiết lập trạng thái rảnh cho đối tượng this.stop();
giúp người dùng thuận tiện hơn nhiều trong quá trình xây dựng bài toán SMT. Còn đối với hệ thống máy trạm, nếu được xây dựng tốt sẽ giúp cho hiệu năng xử lý bài toán SMT sẽ tăng cao, giúp cho hiệu năng toàn hệ thống được cải thiện.
Chương 6. Cài đặt và thử nghiệm 6.1.Cài đặt
Đối với hệ thống máy khách, việc cài đặt sẽ phụ thuộc vào người sử dụng. Do hệ thống được xây dựng như một thư viện người dùng, nên việc đưa vào những đoạn mã chương trình của người dùng là rất dễ dàng. Hệ thống cũng hỗ trợ người dùng thực thi trực tiếp nếu đã có tệp tin chứa nội dung bài toán SMT theo đúng chuẩn của SMT- LIB
Đối với hệ thống máy khách. Java là một ngôn ngữ không phụ thuộc vào hệ điều hành khi biên dịch nên hệ thống có thể cài đặt trên mọi hệ điều hành khác nhau. Tuy nhiên, do hệ thống có sử dụng việc gọi tiến trình và kết xuất dữ liệu, nên việc cài đặt trên hệ điều hành khác nhau cần có sự tùy chỉnh về mã lệnh cũng như tệp tin hỗ trợ.
Trong phần thử nghiệm của nhóm phát triển, hệ thống máy khách được cài đặt trên mộ máy tính có cấu hình phổ biến hiện nay và sử dụng nền hệ điều hành windows. Phần máy trạm sẽ được cài đặt song song hai bộ giải trên hai máy khác nhau: Một máy có nền windows 7 và một máy được cài sẵn hệ điều hành Linux CenOS.
6.2.Bài toán thực nghiệm
Bài toán thực nghiệm được xây dựng nhằm kiểm tra tính đúng đắn cũng như hiệu năng sử dụng của hệ thống.
6.2.1. Xây dựng bài toán SMT dựa trên các hàm API
Từ những hàm API của hệ thống được cài đặt trên máy khách, ta xây dựng một chương trình sử dụng các hàm API đó để xây dựng một bài toán SMT hoàn chỉnh. Việc sử dụng các hàm API được tuân thủ chặt chẽ theo luật và định nghĩa đã được nêu trong phần kiến thức nền tảng và phần hướng đẫn các hàm API.
6.2.2. Thử nghiệm kết nối với máy chủ và toàn hệ thống
Để thử nghiệm hoạt động của toàn hệ thống, nhóm phát triển đã sử dụng một số các tệp tin được xây dựng và đưa ra của SMT-LIB. Cụ thể
Bảng 8 Bảng dữ liệu các tệp tin đầu vào thử nghiệm
Tên tệp tin Kích thước Logic Trạng thái
Test1.SMT 1kb QF_UF sat
Test2.SMT 3kb QF_AX sat
Test3.SMT 1kb QF_AUFLIA sat
Test4.SMT 312kb QF_LIA sat
Test5.SMT 587kb QF_LIA unsat
Test6.SMT 270kb QF_LRA sat
Với tất cả các file dữ liệu kiểm thử được sử dụng như trên, ta tùy chỉnh để hệ thống có thể chạy tối đa 6 kết nối cùng một lúc, số lượng kết nối chờ là 500. Hệ thống cho thấy được khả năng đáp ứng tốt tất cả các yêu cầu của người dùng, đáp ứng kết quả chính xác và thời gian đáp ứng nhanh chóng, hệ thống đưa về kết quả nhanh nhất trong các bộ giải. Dựa vào những file dữ liệu để kiểm thử hệ thống như trên, ta có kết quả của hệ thống về mặt thời gian như sau:
Bảng 9: Kết quả thời gian của thực nghiệm
Solver
Tên file Z3 Yieces All
Test1.smt 1 1 1 Test2.smt 1 1 1 Test3.smt 1 1 1 Test4.smt 7 8 7 Test5.smt 20 16 16 Test6.smt 35 35 35
Bảng 10: Kêt quả trả về của thực nghiệm
Solver
Tên file Z3 Yieces All
Test1.smt Sat Sat Sat
Test2.smt Sat Error Sat
Test3.smt Sat Sat Sat
Test4.smt Sat Sat Sat
Test5.smt Unsat Unsat Unsat
Test6.smt Time out TimeOut TimeOut
Từ kết hai bảng kết quả trên, ta có thể thấy rằng, hiệu quả đem lại của hệ thống giải bài toán SMT là khá rõ ràng. Với những bài toán lớn, rõ ràng thời gian trả về của các bộ giải là rất quan trọng. Tuy nhiên, với việc kết hợp nhiều bộ giải, vấn đề tối ưu hóa thời gian giải bài toán đã được giải quyết. Qua những ví dụ 4, 5 ta thấy rằng thời gian giải của các bộ giải đã có sự chênh lệch, và hệ thống đã đưa ra được thời gian trả về kết quả tối ưu nhất.
Từ bảng 10, ta có thể thấy rằng, hầu hết các ví dụ đều có kết quả giống nhau. Tuy nhiên, trong trường hợp kết quả trả về khác nhau, hệ thống vẫn đưa ra được kết quả đúng đắn nhất cho người sử dụng. Ta thấy rằng, ở ví dụ 2, bộ giải yices đã không thể đưa ra được kết quả (vì không hỗ trợ logic này), tuy nhiên bộ giải Z3 vẫn đưa ra được kết quả cho bài toán. Còn ở ví dụ 6, ta thấy rằng, hệ thống đã hoạt động tốt với chức năng thiết lập thời gian chờ tối đa của người dùng. Hệ thống đã ngắt và trả về cho người sử dụng trạng thái của bài toán mà người dùng gửi lên.
Kết Luận
Với hệ thống giải quyết bài toán SMT hiệu năng cao đã được xây dựng và trình bày ở trên, việc giải quyết các bài toán SMT sẽ dễ dàng và mang lại nhiều hiệu quả hơn rất nhiều cho người sử dụng. Tuy rằng, hệ thống mới chỉ được nghiên cứu và phát triển trong những bước đầu hết sức đơn giản, nhưng những hiệu quả mang lại đã được chứng minh rất rõ ràng trong phần thực nghiệm hệ thống. Do sử dụng nhiều bộ giải cùng một lúc với cùng một bài toán nên ngoài khả năng tối ưu hóa mặt thời gian, hệ thống còn có thể tối ưu hóa khả năng giải quyết bài toán SMT dưới nhiều chuẩn Logic, nền lý thuyết khác nhau.
Với việc xây dựng hệ thống máy khách và máy trạm, cá nhân tôi đã có được những hiểu biết nhất định về cấu trúc bài toán SMT cũng như việc phân luồng các tiến trình để tận dụng được tối đa hiệu năng của máy. Tuy những hiểu biết và thành quả còn nhiều hạn chế, nhưng đây là bước đầu để có thể phát triển hoàn thiện hệ thống giải bài toán SMT hiệu năng cao về sau.
Việc nghiên cứu và phát triển hệ thống giải quyết tối ưu bài toán SMT một cách đầy đủ, hoàn thiện hơn cần sự đầu tư về thời gian, trí óc và nhân sự. Rất mong sẽ có những người cùng phát triển hệ thống về sau để hình thành nên hệ thống giải quyết bài