LINQ có yêu cầu nhiều chu kỳ xử lý và bộ nhớ hơn so với các kỹ thuật lặp dữ liệu cấp thấp hơn không?


8

Lý lịch

Gần đây tôi đang trong quá trình thực hiện các cuộc phỏng vấn công nghệ mệt mỏi cho các vị trí sử dụng ngăn xếp .NET, một số trong đó bao gồm các câu hỏi ngớ ngẩn như câu hỏi này và một số câu hỏi có giá trị hơn. Gần đây tôi đã gặp một vấn đề có thể hợp lệ nhưng tôi muốn kiểm tra với cộng đồng ở đây để chắc chắn.

Khi được một người phỏng vấn hỏi tôi sẽ đếm tần số từ trong tài liệu văn bản và xếp hạng kết quả như thế nào, tôi đã trả lời rằng tôi sẽ

  1. Sử dụng một đối tượng luồng đặt tệp văn bản vào bộ nhớ dưới dạng chuỗi.
  2. Chia chuỗi thành một mảng trên khoảng trắng trong khi bỏ qua dấu chấm câu.
  3. Sử dụng LINQ dựa vào mảng để .GroupBy().Count()sau đó OrderBy()đếm.

Tôi đã trả lời sai vì hai lý do:

  1. Truyền toàn bộ tệp văn bản vào bộ nhớ có thể là thảm họa. Nếu đó là toàn bộ bách khoa toàn thư thì sao? Thay vào đó, tôi nên phát trực tiếp một khối tại một thời điểm và bắt đầu xây dựng bảng băm.
  2. LINQ quá đắt và đòi hỏi quá nhiều chu trình xử lý. Tôi nên đã xây dựng một bảng băm thay thế và, với mỗi lần lặp, chỉ thêm một từ vào bảng băm nếu nó không tồn tại và sau đó tăng nó lên.

Lý do đầu tiên có vẻ, tốt, hợp lý. Nhưng thứ hai cho tôi thêm tạm dừng. Tôi nghĩ rằng một trong những điểm bán hàng của LINQ là nó chỉ đơn giản là trừu tượng hóa các hoạt động cấp thấp hơn như bảng băm, nhưng, dưới tấm màn che, nó vẫn là cách thực hiện tương tự.

Câu hỏi

Bên cạnh một vài chu kỳ xử lý bổ sung để gọi bất kỳ phương pháp trừu tượng, không đòi hỏi LINQ đáng kể chế biến nhiều chu kỳ để hoàn thành một nhiệm vụ lặp dữ liệu nhất định so với một nhiệm vụ cấp thấp hơn (chẳng hạn như việc xây dựng một bảng băm) sẽ?


2
Hỏi anh ta những gì ngu ngốc đặt toàn bộ bách khoa toàn thư trong một tập tin văn bản?
JeffO

4
Đó là một trong những điều cần được đo lường. Xây dựng 2 hoặc 3 triển khai và ghi lại hiệu suất. Khái quát về LINQ hoặc kỹ thuật X không hữu ích. Tôi muốn nói rằng thật tệ khi người phỏng vấn tuyên bố sử dụng LINQ một câu trả lời "sai". Mặc dù trong phía máy chủ tải nặng xử lý mỗi mili giây.
Lord Tydus

1
Một google nhanh chóng cho "linq thử nghiệm hiệu suất cho các đối tượng và vòng lặp" đã tìm thấy khá nhiều lượt truy cập. Một số bao gồm mã nguồn mà bạn có thể sử dụng để tự kiểm tra. Xem này , đâyđây .
Oded

1
Đối với các cuộc phỏng vấn, hãy nhớ rằng có một số lập trình viên C ++ "trường học cũ" nghĩ rằng bạn nên phát minh lại bánh xe hơn là sử dụng các thư viện .NET. Bạn cũng sẽ gặp những VBers trường cũ muốn thực hiện tất cả các mã truy cập dữ liệu bằng tay thay vì sử dụng LINQ và EF.
jfrankcarr

1
Oded, những ví dụ trong các liên kết bạn cung cấp là rất sai. Tôi không thể đi vào tất cả các chi tiết trong một bình luận, nhưng lấy liên kết thứ hai. Nó so sánh "foreach x if x = toFind stop" với truy vấn linq tương đương với "select * từ danh sách trong đó x like toFind" Sự khác biệt là lần đầu tiên dừng khi tìm thấy phiên bản đầu tiên, truy vấn linq luôn lặp lại mỗi mục nhập và sẽ trả về một bộ sưu tập TẤT CẢ các mục phù hợp với mẫu tìm kiếm. Rất khác nhau. Đó không phải là vì LINQ bị hỏng, đó là vì anh ta đã sử dụng truy vấn sai.
Ian

Câu trả lời:


9

Tôi muốn nói rằng điểm yếu chính của câu trả lời này là việc sử dụng Linq ít hơn và nhiều nhà khai thác cụ thể được chọn. GroupByđưa từng yếu tố và chiếu nó vào một khóa và một giá trị đi vào tra cứu. Nói cách khác, mỗi từ sẽ thêm một cái gì đó để tra cứu.

Việc triển khai ngây thơ .GroupBy(e => e)sẽ lưu trữ một bản sao của mỗi từ trong tài liệu nguồn, làm cho việc tra cứu cuối cùng gần như lớn bằng tài liệu gốc. Ngay cả khi chúng tôi chiếu ra giá trị với việc .GroupBy(e => e, e => null)chúng tôi tạo ra một tra cứu lớn các giá trị nhỏ.

Những gì chúng ta muốn là một toán tử chỉ lưu giữ thông tin cần thiết, đó là một bản sao của mỗi từ và số lượng của từ đó cho đến nay. Đối với điều đó, chúng ta có thể sử dụng Aggregate:

words.Aggregate(new Dictionary<string, int>(), (counts, word) => 
{
    int currentCount;
    counts.TryGetValue(word, currentCount);
    counts[word] = currentCount + 1;
    return counts;
} 

Từ đây, có một số cách chúng ta có thể cố gắng để làm điều này nhanh hơn:

  1. Thay vì tạo nhiều chuỗi trong khi phân tách, chúng ta có thể chuyển qua các cấu trúc tham chiếu chuỗi gốc và phân đoạn có chứa từ đó và chỉ sao chép phân đoạn ra khi nó trở thành một khóa duy nhất
  2. Sử dụng Parallel Linq để tổng hợp trên một số lõi sau đó kết hợp các kết quả . Điều này là không đáng kể so với công việc chân cần thiết để làm điều này bằng tay.

Tất cả những điểm tốt Chris, cảm ơn. Tôi sẽ không kiềm chế chấp nhận một chút vì câu hỏi chung chung hơn và về cơ bản được trả lời bởi Oded trong các ý kiến ​​trên. Tôi chỉ muốn cho anh ta cơ hội để cung cấp câu trả lời đầu tiên. Cảm ơn một lần nữa cho cái nhìn sâu sắc của bạn, tuy nhiên, đó là tuyệt vời.
Matt Cashatt

6

Tôi nghĩ rằng bạn đã có một lối thoát hẹp, người phỏng vấn không thực sự biết anh ta đang nói về cái gì. Thậm chí tệ hơn, anh tin rằng có một câu trả lời "đúng". Nếu anh ấy là người mà bạn muốn làm việc, tôi mong anh ấy sẽ trả lời ban đầu của bạn, tìm hiểu lý do tại sao bạn chọn nó, và sau đó thách thức bạn làm cho nó tốt hơn nếu anh ấy có thể tìm ra vấn đề với nó.

LINQ khiến mọi người sợ hãi vì nó trông giống như ma thuật. Nó thực sự rất đơn giản (Đơn giản đến mức bạn cần trở thành một thiên tài để tìm ra nó)

var result = from item in collection where item=>item.Property > 3 select item;

Được tổng hợp thành:

IEnumerable<itemType> result = collection.Where(item=>item.property >3);

(Xin đừng hét nếu tôi mắc lỗi cú pháp, đó là sau nửa đêm và tôi đang ở trên giường :))

Đâu là một phương thức mở rộng trên IEnumerable trong đó có lambda. Lambda chỉ đơn giản là (trong trường hợp này) được biên dịch cho một đại biểu:

bool AMethod(ItemType item)
{
    return item.property >3;
}

Phương thức Where chỉ đơn giản là thêm TẤT CẢ các mục của mục trong đó AMethod trả về giá trị true cho bộ sưu tập được trả về.

Không có lý do nào có thể chậm hơn việc thực hiện một foreach và thêm tất cả các mục phù hợp vào một bộ sưu tập trong vòng lặp đó. Trong thực tế, phương thức mở rộng có lẽ đang làm điều đó. Phép thuật thực sự đến khi bạn cần tiêm một tiêu chí thay thế.

Như tôi đã đề cập ở trên trong nhận xét của tôi, một số ví dụ được liên kết là rất sai. Và đó là loại thông tin sai lệch gây ra vấn đề.

Cuối cùng, nếu cuộc phỏng vấn đã cho bạn một cơ hội, bạn có thể nói rằng:

  • LINQ rất dễ đọc, đặc biệt là nơi bạn bắt đầu giới thiệu các dự đoán và nhóm thú vị. Mã dễ đọc là dễ dàng [y | ier] để duy trì mã là một chiến thắng.

  • Sẽ thực sự dễ dàng để đo lường hiệu suất nếu nó thực sự là một nút cổ chai và thay thế nó bằng một cái gì đó khác.


Nhìn chung, tôi đồng ý với bạn nhưng hành vi của phương thức Where - Phương thức Where không thêm tất cả các mục phù hợp vào bộ sưu tập. Nó lưu trữ thông tin cần thiết để lọc các mục trong cây biểu thức. Nếu trình vòng lặp trả lại không thực sự được sử dụng, sẽ không có quá trình lọc nào xảy ra.
Codism

Điểm tuyệt vời, tôi nên đã đề cập rằng. Trong ví dụ của họ, họ sử dụng trình lặp được trả về. Đây là sự điên rồ trong thử nghiệm của họ. Để trích xuất một giá trị tìm thấy (tất cả các mục trong dữ liệu thử nghiệm của họ là duy nhất), họ đã có một thông báo đã lặp lại vô số kết quả để hiển thị kết quả. Tất nhiên chỉ có một kết quả nên nó chỉ in một câu trả lời. Sự điên rồ :)
Ian

Mặc dù tôi chưa sử dụng LINQ, nhưng một điều tôi cảm thấy khó chịu là tối ưu hóa mọi thứ như Countđối với một vài tình huống hẹp hoạt động kém với đóng gói. Ghép nối một danh sách triệu mục và một trình vòng lặp bốn mục, và Countcần khoảng 5 thao tác, nhưng thay vào đó sẽ yêu cầu một triệu. Tôi ước MS sẽ xác định IEnhancedEnumeratorbằng một int Move(int)phương thức trả về 0 trong trường hợp thành công hoặc trả lại số tiền thiếu trong trường hợp không thành công (do đó, việc thực hiện Move(1000003)trên một List<T>.Enumeratordanh sách mới được tạo ra từ danh sách hàng triệu mục sẽ trả về 3). Bất kỳ triển khai nào ...
supercat

... IEnumerable<T>có thể được gói gọn trong việc triển khai IEnhancedEnumerator, nhưng các loại thực hiện IEnhancedEnumeratortrực tiếp có thể cho phép tăng tốc độ đơn hàng cho nhiều hoạt động, và thậm chí những thứ như sự trở lại từ đó Appendcó thể làm lộ khả năng tìm kiếm nhanh của các thành phần.
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.