Câu hỏi kỹ sư cấp nhập cảnh liên quan đến quản lý bộ nhớ


9

Đã được một vài tháng kể từ khi tôi bắt đầu vị trí là một nhà phát triển phần mềm cấp nhập cảnh. Bây giờ tôi đã qua một số đường cong học tập (ví dụ: ngôn ngữ, biệt ngữ, cú pháp của VB và C #) Tôi bắt đầu tập trung vào các chủ đề bí truyền hơn, để viết phần mềm tốt hơn.

Một câu hỏi đơn giản mà tôi đã trình bày với đồng nghiệp đã được trả lời với câu "Tôi đang tập trung vào những điều sai trái". Mặc dù tôi tôn trọng đồng nghiệp này nhưng tôi không đồng ý rằng đây là một "điều sai trái" cần tập trung vào.

Đây là mã (trong VB) và tiếp theo là câu hỏi.

Lưu ý: Hàm GenerateAlert () trả về một số nguyên.

Dim alertID as Integer = GenerateAlert()
_errorDictionary.Add(argErrorID, NewErrorInfo(Now(), alertID))    

đấu với ...

 _errorDictionary.Add(argErrorID, New ErrorInfo(Now(), GenerateAlert()))

Ban đầu tôi đã viết cái sau và viết lại bằng "Dim alertID" để người khác có thể thấy dễ đọc hơn. Nhưng đây là mối quan tâm và câu hỏi của tôi:

Nếu một người viết điều này với Dim AlertID, trên thực tế, nó sẽ chiếm nhiều bộ nhớ hơn; hữu hạn nhưng nhiều hơn, và phương pháp này có nên được gọi nhiều lần nó có thể dẫn đến một vấn đề không? .NET sẽ xử lý đối tượng này AlertID như thế nào. Bên ngoài .NET nên vứt bỏ thủ công đối tượng sau khi sử dụng (gần cuối phụ).

Tôi muốn đảm bảo tôi trở thành một lập trình viên có kiến ​​thức, không chỉ dựa vào bộ sưu tập rác. Tôi có nghĩ quá không? Tôi đang tập trung vào những điều sai trái?


1
Tôi có thể dễ dàng đưa ra trường hợp rằng anh ấy là 100% vì phiên bản đầu tiên dễ đọc hơn. Tôi muốn trình biên dịch thậm chí có thể quan tâm đến những gì bạn quan tâm. Ngay cả khi nó không, bạn đang tối ưu hóa sớm.
Giàn khoan

6
Tôi không chắc chắn rằng nó thực sự sẽ sử dụng nhiều bộ nhớ hơn với một số nguyên ẩn danh so với một số nguyên có tên. Trong mọi trường hợp, điều này thực sự là tối ưu hóa sớm. Nếu bạn cần lo lắng về hiệu quả ở cấp độ này (tôi gần như chắc chắn là bạn không), bạn có thể cần C ++ thay vì C #. Thật tốt khi hiểu các vấn đề về hiệu suất và những gì xảy ra dưới mui xe, nhưng đây là một cây nhỏ trong một khu rừng lớn.
psr

5
Số nguyên được đặt tên và số nguyên ẩn danh sẽ không sử dụng nhiều bộ nhớ hơn, đặc biệt vì số nguyên ẩn danh chỉ là số nguyên có tên mà BẠN không đặt tên (trình biên dịch vẫn phải đặt tên cho nó). Nhiều nhất, số nguyên được đặt tên sẽ có phạm vi khác nhau để nó có thể sống lâu hơn. Số nguyên ẩn danh sẽ chỉ sống miễn là phương thức cần nó, cái được đặt tên sẽ sống miễn là cha mẹ của nó cần nó.
Joel Etherton

Hãy xem ... Nếu Integer là một lớp, nó sẽ được phân bổ trên heap. Biến cục bộ (trên ngăn xếp rất có thể) sẽ giữ một tham chiếu đến nó. Tham chiếu sẽ được chuyển đến đối tượng errorDixi. Nếu bộ thực thi đang thực hiện đếm tham chiếu (hoặc như vậy), thì khi không có thêm tham chiếu nào, nó (đối tượng) sẽ được giải phóng khỏi heap. Bất cứ điều gì trên ngăn xếp sẽ tự động được "giải quyết" khi phương thức thoát. Nếu đó là một người nguyên thủy, rất có thể nó sẽ kết thúc trên stack.
Paul

Đồng nghiệp của bạn đã đúng: vấn đề được đặt ra bởi câu hỏi của bạn không phải là về tối ưu hóa, mà là về khả năng đọc .
haylem

Câu trả lời:


25

"Tối ưu hóa sớm là gốc rễ của mọi tội lỗi (hoặc ít nhất là phần lớn) trong lập trình." - Donald Knuth

Khi đến lượt đầu tiên của bạn, chỉ cần viết mã của bạn để nó chính xác và sạch sẽ. Nếu sau đó được xác định rằng mã của bạn là quan trọng về hiệu năng (có các công cụ để xác định cái này được gọi là profiler), thì nó có thể được viết lại. Nếu mã của bạn không được xác định là quan trọng về hiệu năng, thì khả năng đọc là quan trọng hơn nhiều.

Có đáng để đào sâu vào các chủ đề về hiệu suất và tối ưu hóa này không? Tuyệt đối, nhưng không phải trên đồng đô la của công ty bạn nếu không cần thiết.


1
Trên đô la của ai khác? Chủ lao động của bạn được hưởng lợi từ sự gia tăng các kỹ năng của bạn hơn là bạn làm.
Marcin

Đối tượng không trực tiếp đóng góp cho nhiệm vụ hiện tại của bạn? Bạn nên theo đuổi những điều này vào thời gian riêng của bạn. Nếu tôi ngồi xuống và nghiên cứu mọi mục CompSci khơi gợi trí tò mò của tôi trong suốt cả ngày, tôi sẽ không làm được gì cả. Đó là những gì buổi tối của tôi là dành cho.
Christopher Berman

2
Kỳ dị. Một số người trong chúng ta có một cuộc sống cá nhân, và như tôi nói, nhà tuyển dụng được hưởng lợi chủ yếu từ nghiên cứu. Điều quan trọng là không thực sự dành cả ngày cho nó.
Marcin

6
Tốt cho bạn. Nó không thực sự làm cho nó một quy tắc phổ quát, mặc dù. Ngoài ra, nếu bạn không khuyến khích nhân viên của mình học tập tại nơi làm việc, tất cả những gì bạn đã làm là không khuyến khích họ học và khuyến khích họ tìm một chủ nhân khác thực sự trả tiền cho việc phát triển nhân viên.
Marcin

2
Tôi hiểu các ý kiến ​​lưu ý trong các ý kiến ​​trên; Tôi muốn lưu ý rằng tôi đã hỏi trong giờ nghỉ trưa. :). Một lần nữa, cảm ơn tất cả các bạn đã đóng góp ở đây và trên toàn bộ trang web Stack Exchange; nó là vô giá!
Sean Hobbs

5

Đối với chương trình .NET trung bình của bạn, vâng, nó quá suy nghĩ. Có thể đôi khi bạn sẽ muốn tìm hiểu các loại hạt và bu lông chính xác những gì đang diễn ra bên trong .NET nhưng điều này tương đối hiếm.

Một trong những quá trình chuyển đổi khó khăn mà tôi có là chuyển từ sử dụng C và MASM sang lập trình trong VB cổ điển từ những năm 90. Tôi đã được sử dụng để tối ưu hóa mọi thứ cho kích thước và tốc độ. Tôi đã phải từ bỏ suy nghĩ này trong hầu hết các phần và để VB làm điều đó để có hiệu quả.


5

Như đồng nghiệp của tôi luôn nói:

  1. Lam cho no hoạt động
  2. Sửa tất cả các lỗi làm cho nó hoạt động hoàn hảo
  3. Làm cho nó RẮN
  4. Áp dụng tối ưu hóa nếu nó hoạt động chậm

Nói cách khác, hãy luôn ghi nhớ KISS (giữ cho nó đơn giản ngu ngốc). Bởi vì kỹ thuật quá mức, suy nghĩ quá mức một số logic mã có thể là một vấn đề để thay đổi logic vào lần tới. Tuy nhiên, giữ mã sạch và đơn giản luôn là cách thực hành tốt .

Tuy nhiên, theo thời gian và kinh nghiệm bạn sẽ biết rõ hơn mã nào có mùi và sẽ cần tối ưu hóa khá sớm.


3

Nên viết cái này với Dim AlertID

Khả năng đọc là quan trọng. Trong ví dụ của bạn, mặc dù tôi không chắc chắn rằng bạn đang thực sự làm cho mọi thứ dễ đọc hơn. GenerateAlert () có một cái tên hay và nó không gây ra nhiều tiếng ồn. Có lẽ có sử dụng tốt hơn thời gian của bạn.

thực tế nó sẽ chiếm nhiều bộ nhớ hơn;

Tôi nghi ngờ nó không. Đó là một tối ưu hóa tương đối đơn giản cho trình biên dịch để thực hiện.

phương pháp này có nên được gọi nhiều lần nó có thể dẫn đến một vấn đề không?

Sử dụng một biến cục bộ làm trung gian sẽ không ảnh hưởng đến bộ thu gom rác. Nếu bộ nhớ mới của GenerateAlert (), thì nó sẽ thành vấn đề. Nhưng điều đó sẽ quan trọng bất kể biến cục bộ hay không.

.NET sẽ xử lý đối tượng này AlertID như thế nào.

AlertID không phải là một đối tượng. Kết quả của GenerateAlert () là đối tượng. AlertID là biến, nếu đó là biến cục bộ đơn giản là không gian được liên kết với phương thức để theo dõi mọi thứ.

Bên ngoài .NET nên vứt bỏ thủ công đối tượng sau khi sử dụng

Đây là một câu hỏi khó hơn phụ thuộc vào bối cảnh liên quan và ngữ nghĩa sở hữu của thể hiện được cung cấp bởi GenerateAlert (). Nói chung, bất cứ điều gì được tạo ra thể hiện nên xóa nó. Chương trình của bạn có thể sẽ trông khác biệt đáng kể nếu nó được thiết kế với mục đích quản lý bộ nhớ thủ công.

Tôi muốn đảm bảo tôi trở thành một lập trình viên có kiến ​​thức, không chỉ chuyển tiếp vào bộ sưu tập rác. Tôi có nghĩ quá không? Tôi đang tập trung vào những điều sai trái?

Một lập trình viên giỏi sử dụng các công cụ có sẵn cho họ, bao gồm cả trình thu gom rác. Thà lật đổ mọi thứ còn hơn là sống lãng quên. Bạn có thể đang tập trung vào những điều sai trái, nhưng vì chúng tôi ở đây, bạn cũng có thể tìm hiểu về nó.


2

Làm cho nó hoạt động, làm cho nó sạch sẽ, làm cho nó RẮN, THÌ làm cho nó hoạt động nhanh như nó cần để làm việc .

Đó nên là thứ tự bình thường của mọi thứ. Ưu tiên hàng đầu của bạn là làm cho một cái gì đó sẽ vượt qua các bài kiểm tra chấp nhận mà bỏ qua các yêu cầu. Đây là ưu tiên hàng đầu của bạn vì nó là ưu tiên hàng đầu của khách hàng của bạn; đáp ứng các yêu cầu chức năng trong thời hạn phát triển. Ưu tiên tiếp theo là viết mã sạch, dễ đọc, dễ hiểu và do đó có thể được duy trì bởi hậu thế của bạn mà không cần bất kỳ WTF nào khi điều đó trở nên cần thiết (gần như không bao giờ là câu hỏi "nếu"; bạn hoặc ai đó sau bạn S go phải đi trở lại và thay đổi / sửa chữa một cái gì đó). Ưu tiên thứ ba là làm cho mã tuân thủ phương pháp RẮN (hoặc GRASP nếu bạn thích), đưa mã vào các phần mô đun, có thể tái sử dụng, có thể thay thế một lần nữa để bảo trì (không chỉ họ có thể hiểu bạn đã làm gì và tại sao, nhưng có những dòng sạch mà tôi có thể phẫu thuật loại bỏ và thay thế các đoạn mã). Ưu tiên cuối cùng là hiệu suất; nếu mã đủ quan trọng để nó phải tuân theo thông số kỹ thuật hiệu suất, thì gần như chắc chắn đủ quan trọng để được làm cho chính xác, sạch sẽ và RẮN trước.

Echoing Christopher (và Donald Knuth), "tối ưu hóa sớm là gốc rễ của mọi tội lỗi". Ngoài ra, loại tối ưu hóa bạn đang xem xét đều nhỏ (tham chiếu đến đối tượng mới của bạn sẽ được tạo trên ngăn xếp cho dù bạn có đặt tên cho nó trong mã nguồn hay không) và thuộc loại có thể không gây ra bất kỳ sự khác biệt nào trong biên dịch IL. Tên biến không được chuyển tiếp vào IL, vì vậy vì bạn đang khai báo biến ngay trước khi sử dụng lần đầu tiên (và có lẽ là duy nhất), tôi sẽ đặt cược một số tiền bia rằng IL giống hệt nhau giữa hai ví dụ của bạn. Vì vậy, đồng nghiệp của bạn là đúng 100%; bạn đang tìm sai chỗ nếu bạn đang xem biến được đặt tên so với khởi tạo nội tuyến để tìm thứ gì đó để tối ưu hóa.

Tối ưu hóa vi mô trong .NET hầu như không bao giờ có giá trị (Tôi đang nói về 99,99% trường hợp). Trong C / C ++, có thể, NẾU bạn biết bạn đang làm gì. Khi làm việc trong môi trường .NET, bạn đã đủ xa khỏi kim loại của phần cứng có chi phí đáng kể trong việc thực thi mã. Vì vậy, cho rằng bạn đã ở trong một môi trường cho thấy bạn đã từ bỏ tốc độ phồng rộp và thay vào đó đang tìm cách viết mã "chính xác", nếu một cái gì đó trong môi trường .NET thực sự không hoạt động đủ nhanh, thì độ phức tạp của nó là quá cao, hoặc bạn nên xem xét song song nó. Dưới đây là một số gợi ý cơ bản để làm theo để tối ưu hóa; Tôi đảm bảo cho bạn năng suất tối ưu hóa (tốc độ đạt được trong thời gian sử dụng) sẽ tăng vọt:

  • Thay đổi hình dạng hàm quan trọng hơn thay đổi các hệ số - Độ phức tạp WRT Big-Oh, bạn có thể giảm một nửa số bước phải thực hiện trong thuật toán N 2 và bạn vẫn có thuật toán phức tạp bậc hai mặc dù nó thực thi trong một nửa thời gian trước đây Nếu đó là giới hạn phức tạp thấp hơn cho loại vấn đề này, thì cũng vậy, nhưng nếu có một giải pháp NlogN, tuyến tính hoặc logarit cho cùng một vấn đề, bạn sẽ đạt được nhiều hơn bằng cách chuyển đổi thuật toán để giảm độ phức tạp hơn là tối ưu hóa vấn đề bạn có.
  • Chỉ vì bạn không thể thấy sự phức tạp không có nghĩa là nó không khiến bạn phải trả giá - Nhiều trong số các lớp lót thanh lịch nhất trong từ hoạt động khủng khiếp (ví dụ: trình kiểm tra chính Regex là một hàm phức tạp theo cấp số nhân, trong khi hiệu quả đánh giá số nguyên tố liên quan đến việc chia số cho tất cả các số nguyên tố nhỏ hơn căn bậc hai của nó theo thứ tự O (Nlog (sqrt (N))). Linq là một thư viện tuyệt vời vì nó đơn giản hóa mã, nhưng không giống như một công cụ SQL, .Net trình biên dịch sẽ không cố gắng tìm ra cách hiệu quả nhất để thực hiện truy vấn của bạn. Bạn phải biết điều gì sẽ xảy ra khi bạn sử dụng một phương thức, và do đó tại sao một phương thức có thể nhanh hơn nếu được đặt sớm hơn (hoặc muộn hơn) trong chuỗi, trong khi sản xuất kết quả tương tự.
  • OTOH, hầu như luôn luôn có sự đánh đổi giữa độ phức tạp nguồn và độ phức tạp thời gian chạy - SelectionSort rất dễ thực hiện; bạn có thể làm điều đó trong 10LOC hoặc ít hơn. MergeSort phức tạp hơn một chút, Quicksort cũng vậy và RadixSort thậm chí còn hơn thế. Nhưng, khi các thuật toán tăng độ phức tạp mã hóa (và do đó thời gian phát triển "trước mắt"), chúng giảm độ phức tạp thời gian chạy; MergeSort và QuickSort là NlogN và RadixSort thường được coi là tuyến tính (về mặt kỹ thuật, đó là NlogM trong đó M là số lớn nhất trong N).
  • Phá vỡ nhanh - Nếu có một kiểm tra có thể được thực hiện với giá rẻ có khả năng là đúng và có nghĩa là bạn có thể tiếp tục, hãy thực hiện kiểm tra đó trước. Ví dụ, nếu thuật toán của bạn chỉ quan tâm đến các số kết thúc bằng 1, 2 hoặc 3, thì trường hợp rất có thể (được cung cấp dữ liệu hoàn toàn ngẫu nhiên) là một số kết thúc bằng một số chữ số khác, vì vậy hãy kiểm tra xem số đó KHÔNG kết thúc bằng 1, 2 hoặc 3, trước khi thực hiện bất kỳ kiểm tra nào để xem liệu số đó kết thúc bằng 1, 2 hay 3. Nếu một đoạn logic yêu cầu A & B và P (A) = 0,9 trong khi P (B) = 0,1, sau đó kiểm tra B trước, trừ khi! A sau đó! B (thích if(myObject != null && myObject.someProperty == 1)) hoặc B mất nhiều hơn 9 lần so với A để đánh giá ( if(myObject != null && some10SecondMethodReturningBool())).
  • Đừng hỏi bất kỳ câu hỏi nào mà bạn đã biết câu trả lời - Nếu bạn có một loạt các điều kiện "rơi vào" và một hoặc nhiều điều kiện đó phụ thuộc vào một điều kiện đơn giản hơn cũng phải được kiểm tra, đừng bao giờ kiểm tra cả hai những độc lập. Ví dụ: nếu bạn có séc yêu cầu A và séc yêu cầu A && B, bạn nên kiểm tra A và nếu đúng, bạn nên kiểm tra B. Nếu! A, thì! A && B, vì vậy đừng bận tâm.
  • Càng nhiều lần bạn làm một cái gì đó, bạn càng nên chú ý đến cách nó được thực hiện - Đây là một chủ đề phổ biến trong phát triển, trên nhiều cấp độ; theo nghĩa phát triển chung, "nếu một nhiệm vụ chung là tốn thời gian hoặc khó khăn, hãy tiếp tục thực hiện nó cho đến khi bạn vừa nản lòng vừa đủ hiểu biết để đưa ra cách tốt hơn". Theo thuật ngữ mã, thuật toán không hiệu quả được chạy càng nhiều lần, bạn sẽ càng đạt được hiệu suất tổng thể bằng cách tối ưu hóa nó. Có các công cụ định hình có thể lấy một cụm nhị phân và các ký hiệu gỡ lỗi của nó và hiển thị cho bạn, sau khi chạy qua một số trường hợp sử dụng, dòng mã nào được chạy nhiều nhất. Những dòng đó, và những dòng chạy những dòng đó, là những gì bạn nên chú ý nhất, bởi vì bất kỳ sự gia tăng nào về hiệu quả bạn đạt được sẽ được nhân lên.
  • Một thuật toán phức tạp hơn trông giống như một thuật toán ít phức tạp hơn nếu bạn ném đủ phần cứng vào nó . Đôi khi bạn phải nhận ra rằng thuật toán của bạn đang tiến đến giới hạn kỹ thuật của hệ thống (hoặc một phần của hệ thống) mà bạn đang chạy nó; từ thời điểm đó nếu cần nhanh hơn, bạn sẽ kiếm được nhiều hơn bằng cách chạy nó trên phần cứng tốt hơn. Điều này cũng áp dụng cho song song; một thuật toán N 2 -complexity, khi chạy trên lõi N, trông tuyến tính. Vì vậy, nếu bạn chắc chắn rằng bạn đã đạt được độ phức tạp thấp hơn bị ràng buộc đối với loại thuật toán bạn đang viết, hãy tìm cách để "phân chia và chinh phục".
  • Nó nhanh khi đủ nhanh - Trừ khi bạn lắp ráp bằng tay để nhắm mục tiêu vào một con chip cụ thể, luôn có thứ gì đó để đạt được. Tuy nhiên, trừ khi bạn MUỐN lắp ráp bằng tay, bạn phải luôn ghi nhớ những gì khách hàng sẽ gọi là "đủ tốt". Một lần nữa, "tối ưu hóa sớm là gốc rễ của mọi tội lỗi"; khi khách hàng của bạn gọi nó đủ nhanh, bạn đã hoàn thành cho đến khi anh ta không nghĩ rằng nó đủ nhanh nữa.

0

Thời gian duy nhất để lo lắng về việc tối ưu hóa sớm là khi bạn biết bạn đang xử lý một thứ gì đó rất lớn hoặc thứ gì đó bạn biết sẽ thực hiện một số lượng lớn thời gian.

Định nghĩa "khổng lồ" rõ ràng thay đổi dựa trên hệ thống mục tiêu của bạn là như thế nào.


0

Tôi thích phiên bản hai dòng đơn giản hơn vì dễ dàng hơn với trình gỡ lỗi. Một dòng với một số cuộc gọi nhúng làm cho nó khó khăn hơn.

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.