Tôi rất muốn tìm hiểu về Scala và có một câu hỏi cơ bản mà tôi dường như không thể tìm thấy câu trả lời: nói chung, có sự khác biệt về hiệu năng và cách sử dụng bộ nhớ giữa Scala và Java không?
Tôi rất muốn tìm hiểu về Scala và có một câu hỏi cơ bản mà tôi dường như không thể tìm thấy câu trả lời: nói chung, có sự khác biệt về hiệu năng và cách sử dụng bộ nhớ giữa Scala và Java không?
Câu trả lời:
Scala làm cho nó rất dễ dàng để sử dụng số lượng lớn bộ nhớ mà không nhận ra nó. Điều này thường rất mạnh mẽ, nhưng đôi khi có thể gây phiền nhiễu. Ví dụ: giả sử bạn có một chuỗi các chuỗi (được gọi array
) và ánh xạ từ các chuỗi đó sang các tệp (được gọi mapping
). Giả sử bạn muốn lấy tất cả các tệp trong bản đồ và đến từ các chuỗi có độ dài lớn hơn hai. Trong Java, bạn có thể
int n = 0;
for (String s: array) {
if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
if (s.length <= 2) continue;
bigEnough[n++] = map.get(s);
}
Phù! Công việc khó khăn. Trong Scala, cách nhỏ gọn nhất để làm điều tương tự là:
val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)
Dễ dàng! Nhưng, trừ khi bạn khá quen thuộc với cách các bộ sưu tập hoạt động, điều bạn có thể không nhận ra là cách làm này đã tạo ra một mảng trung gian bổ sung (với filter
) và một đối tượng bổ sung cho mọi phần tử của mảng ( mapping.get
trả về một lựa chọn). Nó cũng tạo ra hai đối tượng hàm (một cho bộ lọc và một cho bản đồ phẳng), mặc dù đó hiếm khi là một vấn đề lớn vì các đối tượng chức năng là nhỏ.
Về cơ bản, việc sử dụng bộ nhớ ở mức độ nguyên thủy là như nhau. Nhưng các thư viện của Scala có nhiều phương thức mạnh mẽ cho phép bạn tạo ra số lượng lớn các đối tượng (thường là ngắn) rất dễ dàng. Trình thu gom rác thường khá tốt với loại rác đó, nhưng nếu bạn hoàn toàn không biết bộ nhớ nào đang được sử dụng, có lẽ bạn sẽ gặp rắc rối sớm hơn ở Scala so với Java.
Lưu ý rằng mã Scala Game Benchmark Game Scala được viết theo kiểu khá giống Java để có được hiệu năng giống như Java và do đó có cách sử dụng bộ nhớ giống như Java. Bạn có thể làm điều này trong Scala: nếu bạn viết mã của mình trông giống như mã Java hiệu suất cao, thì đó sẽ là mã Scala hiệu suất cao. (Bạn có thể viết nó theo phong cách Scala độc đáo hơn và vẫn có hiệu suất tốt, nhưng nó phụ thuộc vào chi tiết cụ thể.)
Tôi nên thêm rằng mỗi lần dành thời gian cho lập trình, mã Scala của tôi thường nhanh hơn mã Java của tôi vì trong Scala tôi có thể thực hiện các phần không quan trọng về hiệu năng được thực hiện với ít nỗ lực hơn và dành nhiều sự chú ý của tôi để tối ưu hóa các thuật toán và mã cho các phần quan trọng hiệu suất.
Tôi là người dùng mới, vì vậy tôi không thể thêm nhận xét vào câu trả lời của Rex Kerr ở trên (cho phép người dùng mới "trả lời" nhưng không "bình luận" là một quy tắc rất kỳ lạ btw).
Tôi đã đăng ký chỉ đơn giản là để trả lời "phew, Java thật dài dòng và chăm chỉ như vậy" trong câu trả lời phổ biến của Rex ở trên. Trong khi bạn tất nhiên có thể viết mã Scala ngắn gọn hơn, ví dụ Java đưa ra rõ ràng là đầy đủ. Hầu hết các nhà phát triển Java sẽ viết mã như thế này:
List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
if(s.length() > 2 && mapping.get(s) != null) {
bigEnough.add(mapping.get(s));
}
}
Và tất nhiên, nếu chúng ta sẽ giả vờ rằng Eclipse không thực hiện hầu hết việc gõ thực tế cho bạn và mọi ký tự được lưu thực sự làm cho bạn trở thành một lập trình viên tốt hơn, thì bạn có thể viết mã này:
List b=new ArrayList();
for(String s:array)
if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));
Bây giờ tôi không chỉ tiết kiệm thời gian để tôi nhập tên biến đầy đủ và dấu ngoặc nhọn (giải phóng tôi để dành thêm 5 giây để suy nghĩ thuật toán sâu sắc), mà tôi còn có thể nhập mã của mình vào các cuộc thi giấu giếm và có khả năng kiếm thêm tiền những ngày lễ
Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
Viết Scala của bạn như Java và bạn có thể mong đợi mã byte gần như giống hệt nhau được phát ra - với các số liệu gần như giống hệt nhau.
Viết nó "thành ngữ" hơn, với các đối tượng bất biến và các hàm bậc cao hơn, và nó sẽ chậm hơn một chút và lớn hơn một chút. Một ngoại lệ cho quy tắc này là khi sử dụng các đối tượng chung trong đó các tham số loại sử dụng@specialised
chú thích, điều này sẽ tạo ra mã byte lớn hơn thậm chí có thể vượt quá hiệu suất của Java bằng cách tránh đấm bốc / bỏ hộp.
Một điều đáng nói nữa là thực tế là bộ nhớ nhiều hơn / tốc độ thấp hơn là sự đánh đổi không thể tránh khỏi khi viết mã có thể chạy song song. Mã Scala thành ngữ về bản chất có tính khai báo cao hơn nhiều so với mã Java thông thường và thường chỉ có 4 ký tự (.par
) không hoàn toàn song song.
Vì vậy nếu
Sau đó, bạn sẽ nói rằng mã Scala bây giờ chậm hơn 25% hoặc nhanh hơn 3 lần?
Câu trả lời đúng phụ thuộc vào chính xác cách bạn xác định "hiệu suất" :)
.par
trong 2.9.
.par
.
map
phương pháp này sẽ rất nhỏ.
Trò chơi điểm chuẩn ngôn ngữ máy tính:
Kiểm tra tốc độ java / scala 1.71 / 2.25
Kiểm tra bộ nhớ java / scala 66,55 / 80,81
Vì vậy, điểm chuẩn này nói rằng java nhanh hơn 24% và scala sử dụng bộ nhớ nhiều hơn 21%.
Tất cả trong tất cả, đó không phải là vấn đề lớn và không quan trọng trong các ứng dụng trong thế giới thực, nơi phần lớn thời gian được sử dụng bởi cơ sở dữ liệu và mạng.
Điểm mấu chốt: Nếu Scala làm cho bạn và nhóm của bạn (và mọi người thực hiện dự án khi bạn rời đi) hiệu quả hơn, thì bạn nên thực hiện nó.
Những người khác đã trả lời câu hỏi này liên quan đến các vòng lặp chặt chẽ mặc dù dường như có sự khác biệt rõ ràng về hiệu suất giữa các ví dụ của Rex Kerr mà tôi đã nhận xét.
Câu trả lời này thực sự nhắm vào những người có thể điều tra nhu cầu tối ưu hóa chặt chẽ như lỗ hổng thiết kế.
Tôi còn khá mới với Scala (khoảng một năm hoặc lâu hơn) nhưng cảm giác về nó, cho đến nay, là nó cho phép bạn trì hoãn nhiều khía cạnh của thiết kế, thực hiện và thực hiện tương đối dễ dàng (với đủ khả năng đọc và thử nghiệm nền :)
Các tính năng thiết kế hoãn lại:
Các tính năng thực hiện bị trì hoãn:
Các tính năng thực thi bị trì hoãn: (xin lỗi, không có liên kết)
Những tính năng này, với tôi, là những tính năng giúp chúng ta bước đi trên con đường đến với các ứng dụng nhanh, chặt chẽ.
Các ví dụ của Rex Kerr khác nhau về khía cạnh thực thi được hoãn lại. Trong ví dụ Java, việc phân bổ bộ nhớ được hoãn lại cho đến khi kích thước của nó được tính toán trong đó ví dụ Scala trì hoãn việc tra cứu ánh xạ. Đối với tôi, chúng có vẻ như là các thuật toán hoàn toàn khác nhau.
Đây là những gì tôi nghĩ là nhiều hơn một quả táo tương đương với ví dụ Java của mình:
val bigEnough = array.collect({
case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})
Không có bộ sưu tập trung gian, không có Option
trường hợp, v.v ... Điều này cũng bảo tồn loại bộ sưu tập nên bigEnough
loại là Array[File]
- Array
'scollect
thực hiện có thể sẽ được làm một cái gì đó dọc theo dòng của những gì mã Java của ông Kerr làm.
Các tính năng thiết kế bị trì hoãn mà tôi liệt kê ở trên cũng sẽ cho phép các nhà phát triển API bộ sưu tập của Scala triển khai triển khai thu thập cụ thể theo mảng nhanh đó trong các bản phát hành trong tương lai mà không phá vỡ API. Đây là những gì tôi đang đề cập với bước đi trên con đường đến tốc độ.
Cũng thế:
val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)
Các withFilter
phương pháp mà tôi đã sử dụng ở đây thay vì filter
sửa chữa các vấn đề thu thập trung gian nhưng vẫn còn là vấn đề lựa chọn ví dụ.
Một ví dụ về tốc độ thực hiện đơn giản trong Scala là ghi nhật ký.
Trong Java, chúng ta có thể viết một cái gì đó như:
if (logger.isDebugEnabled())
logger.debug("trace");
Trong Scala, đây chỉ là:
logger.debug("trace")
bởi vì tham số thông báo để gỡ lỗi trong Scala có kiểu " => String
" mà tôi nghĩ là hàm không tham số thực thi khi được đánh giá, nhưng tài liệu gọi tên là pass-by-name.
EDIT {Hàm trong Scala là các đối tượng nên có thêm một đối tượng ở đây. Đối với công việc của tôi, trọng lượng của một đối tượng tầm thường đáng để loại bỏ khả năng thông điệp tường trình được đánh giá không cần thiết. }
Điều này không làm cho mã nhanh hơn nhưng nó làm cho nó có khả năng nhanh hơn và chúng ta ít có kinh nghiệm trải qua và dọn dẹp mã của người khác.
Đối với tôi, đây là một chủ đề nhất quán trong Scala.
Mã cứng không nắm bắt được tại sao Scala nhanh hơn mặc dù nó gợi ý một chút.
Tôi cảm thấy đó là sự kết hợp giữa việc sử dụng lại mã và mức chất lượng mã trong Scala.
Trong Java, mã tuyệt vời thường bị buộc phải trở thành một mớ hỗn độn không thể hiểu được và do đó không thực sự khả thi trong các API chất lượng sản xuất vì hầu hết các lập trình viên sẽ không thể sử dụng nó.
Tôi rất hy vọng rằng Scala có thể cho phép các einstein trong chúng ta triển khai các API có thẩm quyền hơn nhiều, có khả năng được thể hiện thông qua DSL. Các API cốt lõi trong Scala đã đi xa trên con đường này.
@higherkinded trình bày về chủ đề về chủ đề - Cân nhắc hiệu suất Scala thực hiện một số so sánh Java / Scala.
Công cụ:
Blogpost tuyệt vời:
Cả Java và Scala đều biên dịch thành mã byte JVM, vì vậy sự khác biệt không lớn lắm. Sự so sánh tốt nhất bạn có thể nhận được có lẽ là trên trò chơi điểm chuẩn ngôn ngữ máy tính , về cơ bản nói rằng cả Java và Scala đều có cùng mức sử dụng bộ nhớ. Scala chỉ một chút chậm hơn so với Java trên một số điểm chuẩn được liệt kê, nhưng điều đó có thể đơn giản là do việc triển khai các chương trình là khác nhau.
Thực sự, cả hai đều rất thân thiết, không đáng lo ngại. Việc tăng năng suất mà bạn có được bằng cách sử dụng một ngôn ngữ biểu cảm hơn như Scala đáng giá hơn rất nhiều so với hiệu suất tối thiểu (nếu có).
Java and Scala both compile down to JVM bytecode,
được kết hợp với một so
tuyên bố trong câu hỏi diffence isn't that big.
mà tôi muốn chỉ ra, đó so
chỉ là một thủ thuật tu từ, và không phải là một kết luận tranh luận.
Ví dụ Java thực sự không phải là một thành ngữ cho các chương trình ứng dụng điển hình. Mã tối ưu hóa như vậy có thể được tìm thấy trong một phương pháp thư viện hệ thống. Nhưng sau đó, nó sẽ sử dụng một mảng đúng loại, tức là Tệp [] và sẽ không ném IndexOutOfBoundException. (Điều kiện lọc khác nhau để đếm và thêm). Phiên bản của tôi sẽ là (luôn luôn (!) Với các dấu ngoặc nhọn vì tôi không muốn mất một giờ để tìm kiếm một lỗi được giới thiệu bằng cách lưu 2 giây để nhấn một phím duy nhất trong Eclipse):
List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
if(s.length() > 2) {
File file = mapping.get(s);
if (file != null) {
bigEnough.add(file);
}
}
}
Nhưng tôi có thể mang đến cho bạn rất nhiều ví dụ mã Java xấu xí khác từ dự án hiện tại của tôi. Tôi đã cố gắng tránh các bản sao chung và sửa đổi phong cách mã hóa bằng cách bao gồm các cấu trúc và hành vi chung.
Trong lớp cơ sở DAO trừu tượng của tôi, tôi có một lớp bên trong trừu tượng cho cơ chế lưu trữ chung. Đối với mỗi loại đối tượng mô hình cụ thể, có một lớp con của lớp cơ sở DAO trừu tượng, trong đó lớp bên trong được phân lớp để cung cấp một triển khai cho phương thức tạo đối tượng kinh doanh khi được tải từ cơ sở dữ liệu. (Chúng tôi không thể sử dụng công cụ ORM vì chúng tôi truy cập hệ thống khác thông qua API độc quyền.)
Mã phân lớp và mã khởi tạo này hoàn toàn không rõ ràng trong Java và sẽ rất dễ đọc trong Scala.