2010-09-02 27 views
16

Tôi đang chạy mã scala này trên 32-bit quad-core Core2 hệ thống:Tại sao scala tương lai của tôi không hiệu quả hơn?

def job(i:Int,s:Int):Long = { 
    val r=(i to 500000000 by s).map(_.toLong).foldLeft(0L)(_+_) 
    println("Job "+i+" done") 
    r 
} 

import scala.actors.Future 
import scala.actors.Futures._ 

val JOBS=4 

val jobs=(0 until JOBS).toList.map(i=>future {job(i,JOBS)}) 
println("Running...") 
val results=jobs.map(f=>f()) 
println(results.foldLeft(0L)(_+_)) 

(Vâng, tôi làm biết có nhiều cách hiệu quả hơn để tổng hợp một loạt các số nguyên; nó chỉ để cung cấp cho CPU một cái gì đó để làm).

Tùy thuộc vào những gì tôi thiết lập JOBS để, mã chạy trong những thời điểm sau:

JOBS=1 : 31.99user 0.84system 0:28.87elapsed 113%CPU 
JOBS=2 : 27.71user 1.12system 0:14.74elapsed 195%CPU 
JOBS=3 : 33.19user 0.39system 0:13.02elapsed 257%CPU 
JOBS=4 : 49.08user 8.46system 0:22.71elapsed 253%CPU 

Tôi ngạc nhiên rằng điều này không thực sự mở rộng vượt ra ngoài 2 tương lai "trong vở kịch". Tôi làm rất nhiều mã C++ đa luồng và không nghi ngờ gì tôi sẽ có được quy mô lớn lên đến 4 lõi và xem> sử dụng CPU 390% nếu tôi mã hóa loại điều này với TBB của Intel hoặc boost::threads (nó sẽ tiết lộ chi tiết hơn khóa học).

Vì vậy: điều gì đang xảy ra và làm cách nào để tôi có thể thu được 4 lõi mà tôi mong đợi? Điều này có bị hạn chế bởi một thứ gì đó trong scala hoặc JVM không? Nó xảy ra với tôi tôi không thực sự biết "nơi" scala tương lai chạy ... là một sợi sinh ra trong tương lai, hoặc không "tương lai" cung cấp một hồ bơi thread chuyên dụng để chạy chúng?

[Tôi đang sử dụng scala 2.7.7 gói từ Debian/Bóp trên một hệ thống với ánh nắng mặt trời-java6 (6-20-0lennny1) Lenny.]

Cập nhật:

Như đã đề cập trong câu trả lời của Rex, tôi đã mã hóa để tránh tạo đối tượng.

def job(i:Long,s:Long):Long = { 
    var t=0L 
    var v=i 
    while (v<=10000000000L) { 
    t+=v 
    v+=s 
    } 
    println("Job "+i+" done") 
    t 
} 
// Rest as above... 

Điều này đã nhanh hơn rất nhiều tôi phải tăng đáng kể số lần lặp lại để chạy trong một khoảng thời gian! Kết quả là:

JOBS=1: 28.39user 0.06system 0:29.25elapsed 97%CPU 
JOBS=2: 28.46user 0.04system 0:14.95elapsed 190%CPU 
JOBS=3: 24.66user 0.06system 0:10.26elapsed 240%CPU 
JOBS=4: 28.32user 0.12system 0:07.85elapsed 362%CPU 

giống như những gì tôi muốn thấy (mặc dù 3 trường hợp công việc hơi kỳ quặc, chỉ với một nhiệm vụ hoàn thành một vài giây trước hai bước còn lại).

Đẩy nó thêm một chút, trên một quad-core hyperthreaded i7 phiên bản sau với JOBS=8 đạt được một tăng tốc x4.4 vs JOBS = 1, với 571% CPU sử dụng.

+1

Bạn đang thiếu kiên nhẫn, muốn tương lai ngay hôm nay! Nghiêm túc, Rex nhấn móng tay trên đầu, bạn đang chuẩn hóa thu gom rác thải, không hiệu quả tương lai. –

+1

Heh ... quá đúng.Khi tôi gửi câu hỏi này, tôi đã không sử dụng Scala từ lâu, và có lẽ hơi quá đáng tin cậy về một số sự cường điệu cực đoan xung quanh nó. – timday

+0

cẩn thận để chạy lại thử nghiệm với akka.dispatch.Future? –

Trả lời

14

Tôi đoán là bộ thu gom rác đang thực hiện nhiều công việc hơn là tính năng bổ sung. Vì vậy, bạn bị giới hạn bởi những gì người thu gom rác có thể quản lý. Thử chạy lại bài kiểm tra bằng thứ gì đó không tạo bất kỳ đối tượng nào (ví dụ: sử dụng vòng lặp while thay vì phạm vi/bản đồ/gấp). Bạn cũng có thể chơi với các tùy chọn GC song song nếu ứng dụng thực sự của bạn sẽ nhấn GC này rất nhiều.

+0

Yup, điều đó có vẻ như vậy; xem phiên bản mã 2 và kết quả trong bản cập nhật câu hỏi. Vấn đề ban đầu xuất hiện trong một số mã khiến việc sử dụng BigInts nặng nên không có nhiều cơ hội để loại bỏ việc tạo đối tượng ở đó. Đã không đánh giá mức độ ảnh hưởng của loại công cụ này có thể ... scala dường như loại bỏ sự cần thiết phải có nhiều mã mới rõ ràng trong mã nên rất dễ quên nó vẫn còn đó. – timday

+0

Không nên trình biên dịch tối ưu hóa 'mới' này đi? –

+2

@Elazar - Cuối cùng với chuyên môn, nó _may_ có thể cho rằng (hoặc một cái gì đó tương tự) để chạy mà không cần tạo đối tượng. Bây giờ, mặc dù, nó là không thể tránh khỏi: mã là chung chung, vì vậy bạn phải tạo ra các đối tượng cho nó để làm việc ngay cả khi nó chỉ là một wrapper trên một nguyên thủy. –

2

Hãy thử

(i to 500000000 by s).view.map(_.toLong).foldLeft(0L)(_+_) 

Việc áp dụng view là nghĩa vụ phải (như tôi hiểu id) để tránh lặp lại lặp đi lặp lại và tạo đối tượng bằng cách cung cấp giấy gói đơn giản.

Cũng lưu ý rằng bạn có thể sử dụng reduceLeft(_+_) thay vì gấp.

+0

Tôi vẫn ở 2.7.7; view không phải là thành viên của Range cho tôi (hy vọng 2.8 sẽ xuất hiện trong kho lưu trữ Debian một ngày nào đó; tôi lười biếng để xây dựng từ nguồn và tất cả các cuốn sách Scala mà tôi có khoảng 2.7). Vui mừng khi thấy có một số cải tiến được thực hiện trong lĩnh vực này mặc dù. (Và có, tôi thường muốn giảm gấp nếu không có lý do chính đáng; trong trường hợp này là do không có sự đảm bảo nào về các thông số i/s cực đối với hàm sẽ không dẫn đến một hoặc 0 phần tử cho hoạt động giảm). – timday

+0

Yea, tôi cũng nhận thấy điều đó. Bạn có thể tải xuống Scala 2.8.1 trong tgz mà bạn chỉ cần giải nén ở mọi nơi (ví dụ: '/ usr/share /'). Sau đó tạo các liên kết tượng trưng đến các kịch bản trong thư mục con 'bin' và bạn là vàng. Tôi thực sự muốn được quan tâm làm thế nào ba biến thể thực hiện trên 2.8.1 trên máy tính của bạn (tôi không có một quad-core bản thân mình). – Raphael

Các vấn đề liên quan