ทำงานผ่านหลักการความรับผิดชอบเดี่ยว (SRP) ใน Python เมื่อการโทรมีราคาแพง


12

บางจุดฐาน:

  • การเรียกใช้เมธอด Python นั้น "แพง" เนื่องจากเป็นลักษณะที่ตีความได้ ในทางทฤษฎีถ้ารหัสของคุณก็พอง่ายที่จะหมดสภาพรหัสงูใหญ่มีผลกระทบเชิงลบที่นอกเหนือจากการอ่านและนำมาใช้ใหม่ ( ซึ่งเป็นกำไรที่ยิ่งใหญ่สำหรับนักพัฒนาไม่มากสำหรับผู้ใช้ )
  • หลักการความรับผิดชอบเดี่ยว (SRP) ช่วยให้สามารถอ่านโค้ดง่ายต่อการทดสอบและบำรุงรักษา
  • โครงการมีพื้นหลังแบบพิเศษที่เราต้องการให้สามารถอ่านโค้ดการทดสอบและประสิทธิภาพของเวลาได้

ตัวอย่างเช่นโค้ดเช่นนี้ซึ่งเรียกใช้หลายวิธี (x4) จะช้ากว่าวิธีต่อไปนี้ซึ่งเป็นวิธีเดียว

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

เมื่อเปรียบเทียบกับสิ่งนี้:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

ถ้าฉันจะทำขนานอะไรบางอย่างเช่นนั้นมันเป็นเป้าหมายที่ฉันเสียประสิทธิภาพ ใจนั่นเป็นเพียงตัวอย่าง; โปรเจ็กต์ของฉันมีมินิรูทีนหลายตัวพร้อมคณิตศาสตร์เช่นนั้น - แม้ว่าจะง่ายกว่ามากในการทำงานกับโปรไฟล์ของเราไม่ชอบเลย


เราจะยอมรับ SRP ได้อย่างไรและที่ไหนโดยไม่ลดทอนประสิทธิภาพใน Python เนื่องจากการใช้งานโดยธรรมชาติจะส่งผลโดยตรง

มีวิธีแก้ไขปัญหาเช่นตัวประมวลผลล่วงหน้าบางประเภทที่วางสิ่งต่าง ๆ ไว้ในสายการผลิตหรือไม่?

หรือ Python ยากจนในการจัดการการแยกรหัสทั้งหมด?



19
สำหรับสิ่งที่คุ้มค่าตัวอย่างโค้ดทั้งสองของคุณไม่แตกต่างกันตามจำนวนความรับผิดชอบ SRP ไม่ใช่วิธีการนับการฝึก
Robert Harvey

2
@ RobertHarvey คุณพูดถูกขอโทษสำหรับตัวอย่างที่ไม่ดีและฉันจะแก้ไขให้ดีขึ้นเมื่อฉันมีเวลา ไม่ว่าจะเป็นกรณีใดการอ่านและการบำรุงรักษาจะประสบและในที่สุด SRP ก็จะแตกสลายภายใน codebase เมื่อเราตัดคลาสและวิธีการลง
lucasgcb

4
โปรดทราบว่าการใช้งานฟังก์ชั่นนั้นมีราคาแพงในทุกภาษาแม้ว่าคอมไพเลอร์ AOT จะมีความหรูหราของอินไลน์
Eevee

6
ใช้ JITted implementation ของ python เช่น PyPy ควรแก้ไขปัญหานี้เป็นส่วนใหญ่
Bakuriu

คำตอบ:


17

Python ยากจนในการจัดการการแยกรหัสทั้งหมดหรือไม่

น่าเสียดายใช่แล้ว Python ทำงานช้าและมีเกร็ดเล็กเกร็ดน้อยมากมายเกี่ยวกับผู้คนที่เพิ่มประสิทธิภาพอย่างมากด้วยการฝังฟังก์ชั่นและทำให้รหัสของพวกเขาน่าเกลียด

มีวิธีแก้ไขคือ Cython ซึ่งเป็นเวอร์ชันที่รวบรวมของ Python และเร็วกว่ามาก

- แก้ไขฉันแค่ต้องการพูดถึงความคิดเห็นและคำตอบอื่น ๆ แม้ว่าแรงผลักดันของพวกเขาไม่ได้เป็นงูหลามที่เฉพาะเจาะจง แต่การเพิ่มประสิทธิภาพทั่วไปมากขึ้น

  1. อย่าปรับให้เหมาะสมจนกว่าคุณจะประสบปัญหาแล้วมองหาคอขวด

    คำแนะนำที่ดีโดยทั่วไป แต่ข้อสันนิษฐานก็คือรหัส 'ปกติ' มักจะมีประสิทธิภาพ ไม่เป็นเช่นนั้นเสมอไป ภาษาและกรอบงานของแต่ละคนแต่ละคนมีความเป็นตัวของตัวเอง ในกรณีนี้ฟังก์ชั่นการโทร

  2. เพียงไม่กี่มิลลิวินาทีสิ่งอื่น ๆ จะช้าลง

    หากคุณกำลังเรียกใช้รหัสของคุณบนคอมพิวเตอร์เดสก์ท็อปที่ทรงพลังคุณอาจไม่สนใจตราบใดที่รหัสผู้ใช้เดียวของคุณรันในไม่กี่วินาที

    แต่รหัสธุรกิจมีแนวโน้มที่จะทำงานสำหรับผู้ใช้หลายคนและต้องการมากกว่าหนึ่งเครื่องเพื่อรองรับการโหลด หากรหัสของคุณทำงานเร็วเป็นสองเท่าหมายความว่าคุณสามารถมีจำนวนผู้ใช้เป็นสองเท่าหรือครึ่งหนึ่งของจำนวนเครื่อง

    หากคุณเป็นเจ้าของเครื่องจักรและศูนย์ข้อมูลของคุณโดยทั่วไปแล้วคุณจะมีพลังงานก้อนใหญ่ในการใช้พลังงานของ CPU หากรหัสของคุณทำงานช้าคุณสามารถดูดซับได้อย่างน้อยก็จนกว่าคุณจะต้องซื้อเครื่องที่สอง

    ในยุคนี้การคำนวณแบบคลาวด์ที่คุณใช้พลังงานการคำนวณที่คุณต้องการเท่านั้นและไม่มากมีค่าใช้จ่ายโดยตรงสำหรับรหัสที่ไม่ใช่นักแสดง

    การปรับปรุงประสิทธิภาพสามารถลดค่าใช้จ่ายหลักสำหรับธุรกิจที่ใช้ระบบคลาวด์ได้อย่างมากและประสิทธิภาพควรอยู่ด้านหน้าและศูนย์กลาง


1
ในขณะที่คำตอบของ Robert ช่วยครอบคลุมบางฐานสำหรับความเข้าใจผิดที่อาจเกิดขึ้นหลังการเพิ่มประสิทธิภาพแบบนี้ (ซึ่งเหมาะกับคำถามนี้ ) แต่ฉันรู้สึกว่านี่เป็นคำตอบของสถานการณ์ที่ค่อนข้างตรงและสอดคล้องกับบริบท Python มากขึ้น
lucasgcb

2
ขออภัยมันค่อนข้างสั้น ฉันไม่มีเวลาเขียนมากขึ้น แต่ฉันคิดว่าโรเบิร์ตผิดกับอันนี้ คำแนะนำที่ดีที่สุดกับ python ดูเหมือนจะเป็นโปรไฟล์เมื่อคุณใช้รหัส อย่าคิดว่ามันจะเป็นนักแสดงและปรับให้เหมาะสมถ้าคุณพบปัญหา
Ewan

2
@Ewan: คุณไม่ต้องเขียนโปรแกรมทั้งหมดก่อนเพื่อทำตามคำแนะนำของฉัน วิธีการหนึ่งหรือสองนั้นเพียงพอสำหรับการทำโปรไฟล์ที่เพียงพอ
Robert Harvey

1
คุณสามารถลอง pypy ซึ่งเป็นงูหลาม JITted
Eevee

2
@Ewan หากคุณกังวลเกี่ยวกับค่าใช้จ่ายในการโทรฟังก์ชั่นมากเกินไปสิ่งที่คุณกำลังทำอยู่อาจไม่เหมาะกับงูใหญ่ แต่แล้วฉันก็ไม่สามารถนึกถึงตัวอย่างมากมายที่นั่น ส่วนใหญ่ของรหัสธุรกิจคือ IO จำกัด และมักจะมีการจัดการกับสิ่งที่หนักหน่วงของ CPU โดยการเรียกไปยังไลบรารี่ดั้งเดิม
Voo

50

ความกังวลเรื่องประสิทธิภาพที่อาจเกิดขึ้นจริงไม่ใช่ปัญหาในทางปฏิบัติ ปัญหาที่คุณเพิ่มอาจเป็นหนึ่งในนั้น ในภาษาพื้นเมืองเราเรียกว่ากังวลเกี่ยวกับปัญหาเหล่านั้นโดยไม่มีข้อพิสูจน์ว่าเป็นปัญหาที่เกิดขึ้นจริงก่อนการปรับให้เหมาะสม

หากคุณกำลังเขียนส่วนหน้าสำหรับบริการบนเว็บประสิทธิภาพของคุณจะไม่ได้รับผลกระทบอย่างมีนัยสำคัญจากการเรียกใช้ฟังก์ชั่นเนื่องจากค่าใช้จ่ายในการส่งข้อมูลผ่านเครือข่ายเกินเวลาที่ใช้ในการโทรออกด้วยวิธี

หากคุณกำลังเขียนลูปแน่นที่รีเฟรชหน้าจอวิดีโอหกสิบครั้งต่อวินาทีก็อาจมีความสำคัญ แต่ ณ จุดนั้นฉันอ้างว่าคุณมีปัญหามากขึ้นถ้าคุณพยายามใช้ Python เพื่อทำสิ่งนั้นงานที่ Python อาจไม่เหมาะสม

เช่นเคยวิธีที่คุณค้นพบคือการวัด เรียกใช้ตัวสร้างประสิทธิภาพหรือตัวจับเวลาบางส่วนบนรหัสของคุณ ดูว่าเป็นปัญหาจริงหรือไม่ในทางปฏิบัติ


หลักการความรับผิดชอบเดี่ยวไม่ใช่กฎหมายหรือข้อบังคับ มันเป็นแนวทางหรือหลักการ การออกแบบซอฟต์แวร์มักเกี่ยวกับการแลกเปลี่ยน ไม่มีสัมบูรณ์ ไม่ใช่เรื่องแปลกที่จะแลกเปลี่ยนความสามารถในการอ่านและ / หรือการบำรุงรักษาเพื่อความเร็วดังนั้นคุณอาจต้องเสียสละ SRP บนแท่นบูชาของประสิทธิภาพ แต่อย่าทำให้เสียเปรียบถ้าคุณรู้ว่าคุณมีปัญหาเรื่องประสิทธิภาพ


3
ฉันคิดว่านี่เป็นความจริงจนกระทั่งเราคิดค้นคอมพิวเตอร์คลาวด์ ตอนนี้หนึ่งในสองฟังก์ชั่นมีประสิทธิภาพมีค่าใช้จ่ายมากเท่ากับ 4 เท่า
Ewan

2
@Ewan 4 ครั้งอาจไม่สำคัญจนกว่าคุณจะวัดว่ามันสำคัญพอที่จะใส่ใจ ถ้า Foo ใช้เวลา 1 ms และ Bar ใช้เวลา 4 ms นั่นไม่ดี จนกว่าคุณจะทราบว่าการส่งข้อมูลข้ามเครือข่ายนั้นใช้เวลา 200 ms ณ จุดนี้บาร์ที่ทำงานช้าลงไม่ได้สำคัญอะไรนัก (เพียงตัวอย่างเดียวที่เป็นไปได้ที่การลดความเร็ว X ช้าลงนั้นไม่ได้สร้างความแตกต่างที่เห็นได้ชัดหรือมีผลกระทบซึ่งไม่ได้หมายความว่าจำเป็นต้องสมจริงอย่างยิ่ง)
Becuzz

8
@Ewan หากการลดค่าใช้จ่ายช่วยให้คุณประหยัด $ 15 / เดือน แต่จะต้องใช้ผู้รับเหมา $ 125 / ชั่วโมง 4 ชั่วโมงในการแก้ไขและทดสอบฉันสามารถพิสูจน์ได้อย่างง่ายดายว่าไม่คุ้มค่ากับเวลาทำธุรกิจ (หรืออย่างน้อยก็ทำไม่ถูก) ตอนนี้ถ้าเวลาไปตลาดเป็นสิ่งสำคัญ ฯลฯ ) มีการแลกเปลี่ยนกันอยู่เสมอ และสิ่งที่สมเหตุสมผลในสถานการณ์หนึ่งอาจไม่เป็นอย่างอื่น
Becuzz

3
ค่า AWS ของคุณต่ำมาก
Ewan

6
@Ewan AWS ปัดเศษขึ้นไปที่เพดานโดยวิธีการใด ๆ (มาตรฐานคือ 100ms) ซึ่งหมายความว่าการเพิ่มประสิทธิภาพประเภทนี้จะช่วยให้คุณประหยัดทุกอย่างถ้ามันหลีกเลี่ยงการผลักดันคุณไปยังก้อนถัดไปอย่างสม่ำเสมอ
Delioth

2

ก่อนอื่นให้ความกระจ่าง: Python เป็นภาษา มีล่ามที่แตกต่างกันหลายตัวซึ่งสามารถรันโค้ดที่เขียนด้วยภาษา Python การประยุกต์ใช้การอ้างอิง (CPython) มักเป็นสิ่งที่ถูกอ้างอิงเมื่อมีคนพูดถึง "Python" ราวกับว่าเป็นการใช้งาน แต่สิ่งสำคัญคือต้องมีความแม่นยำเมื่อพูดถึงลักษณะของประสิทธิภาพเนื่องจากสามารถแตกต่างกันอย่างมากระหว่างการใช้งาน

เราจะยอมรับ SRP ได้อย่างไรและที่ไหนโดยไม่ลดทอนประสิทธิภาพใน Python เนื่องจากการใช้งานโดยธรรมชาติจะส่งผลโดยตรง

กรณีที่ 1) ถ้าคุณมีรหัส Python แท้ (<= Python Language version 3.5, 3.6 มี "การสนับสนุนระดับเบต้า") ซึ่งอาศัยเพียงโมดูล Python แท้เท่านั้นคุณสามารถใช้ SRP ได้ทุกที่และใช้ PyPy เพื่อเรียกใช้ PyPy ( https://morepypy.blogspot.com/2019/03/pypy-v71-released-now-uses-utf-8.html ) เป็นล่าม Python ซึ่งมี Just In Time Compiler (JIT) และสามารถลบฟังก์ชันได้ เรียกค่าใช้จ่ายตราบเท่าที่มีเวลาเพียงพอที่จะ "อุ่นเครื่อง" โดยการติดตามรหัสที่ดำเนินการ (ไม่กี่วินาที IIRC) **

หากคุณถูก จำกัด ให้ใช้ล่าม CPython คุณสามารถแยกฟังก์ชั่นช้าลงในส่วนขยายที่เขียนด้วย C ซึ่งจะรวบรวมไว้ล่วงหน้าและไม่ต้องทนทุกข์จากค่าใช้จ่ายของล่าม คุณยังสามารถใช้ SRP ได้ทุกที่ แต่รหัสของคุณจะถูกแบ่งระหว่าง Python และ C ไม่ว่าจะดีกว่าหรือแย่กว่าสำหรับการบำรุงรักษามากกว่าการเลือกทิ้ง SRP แต่การติดกับรหัส Python เท่านั้นขึ้นอยู่กับทีมของคุณ แต่ถ้าคุณมีประสิทธิภาพ รหัสมันจะเร็วกว่าสงสัยแม้แต่รหัส Python บริสุทธิ์ที่ได้รับการปรับปรุงโดย CPython ห้องสมุดคณิตศาสตร์ที่เร็วที่สุดหลายแห่งของไพ ธ อนใช้วิธีนี้ (numpy and scipy IIRC) ข้อไหนดีในกรณีที่ 2 ...

กรณีที่ 2) หากคุณมีรหัส Python ซึ่งใช้ส่วนขยาย C (หรือขึ้นอยู่กับไลบรารีที่ใช้ส่วนขยาย C) PyPy อาจมีหรือไม่มีประโยชน์ทั้งนี้ขึ้นอยู่กับวิธีเขียน ดูรายละเอียดเพิ่มเติมที่http://doc.pypy.org/en/latest/extending.htmlแต่สรุปคือ CFFI มีค่าใช้จ่ายน้อยที่สุดขณะที่ CTypes ช้าลง (การใช้งานกับ PyPy อาจช้ากว่า CPython)

Cython ( https://cython.org/ ) เป็นอีกทางเลือกหนึ่งที่ฉันไม่มีประสบการณ์มากนัก ฉันพูดถึงมันเพื่อความสมบูรณ์เพื่อให้คำตอบของฉันสามารถ "ยืนอยู่กับตัวเอง" แต่ไม่อ้างความเชี่ยวชาญใด ๆ จากการใช้งานที่ จำกัด ของฉันรู้สึกว่าฉันต้องทำงานหนักขึ้นเพื่อรับการปรับปรุงความเร็วเดียวกันฉันสามารถรับ "ฟรี" กับ PyPy และถ้าฉันต้องการบางสิ่งที่ดีกว่า PyPy มันก็ง่ายที่จะเขียนส่วนขยาย C ของฉันเอง ( ซึ่งมีประโยชน์ถ้าฉันใช้รหัสที่อื่นหรือแยกส่วนของมันลงในห้องสมุดรหัสของฉันทั้งหมดยังคงสามารถทำงานภายใต้ Python Interpreter ใด ๆ และไม่จำเป็นต้องเรียกใช้โดย Cython)

ฉันกลัวว่าจะถูก "ถูกล็อค" ใน Cython ในขณะที่โค้ดใด ๆ ที่เขียนสำหรับ PyPy สามารถทำงานภายใต้ CPython ได้เช่นกัน

** หมายเหตุเพิ่มเติมบางประการเกี่ยวกับ PyPy ในการผลิต

ระวังให้มากเกี่ยวกับการเลือกตัวเลือกที่มีผลในทางปฏิบัติของ "ล็อคคุณใน" เพื่อ PyPy ใน codebase ขนาดใหญ่ เนื่องจากห้องสมุดบุคคลที่สาม (เป็นที่นิยมและมีประโยชน์) บางแห่งไม่เล่นได้ดีด้วยเหตุผลที่กล่าวถึงก่อนหน้านี้จึงอาจทำให้เกิดการตัดสินใจที่ยากมากในภายหลังหากคุณรู้ว่าคุณต้องการห้องสมุดเหล่านี้ ประสบการณ์ของฉันคือการใช้ PyPy เพื่อเร่งความเร็วไมโครสโคปบางส่วน (แต่ไม่ทั้งหมด) ที่ไวต่อประสิทธิภาพในสภาพแวดล้อมของ บริษัท ซึ่งเพิ่มความซับซ้อนเล็กน้อยให้กับสภาพแวดล้อมการผลิตของเรา (เรามีหลายภาษาที่ใช้งานอยู่แล้ว 3.5 ทำงานต่อไป)

ฉันพบว่าการใช้ทั้ง PyPy และ CPython บังคับให้ฉันต้องเขียนรหัสซึ่งขึ้นอยู่กับการรับประกันที่ทำโดยสเปคภาษาเองเท่านั้นและไม่เกี่ยวกับรายละเอียดการใช้งานที่อาจเปลี่ยนแปลงได้ตลอดเวลา คุณอาจคิดว่ารายละเอียดดังกล่าวเป็นภาระพิเศษ แต่ฉันคิดว่ามันมีค่าในการพัฒนาอาชีพของฉันและฉันคิดว่ามันเป็น "สุขภาพดี" สำหรับระบบนิเวศของ Python โดยรวม


ใช่ ฉันกำลังพิจารณาการมุ่งเน้นที่ส่วนขยาย C สำหรับกรณีนี้แทนที่จะละทิ้งหลักการและเขียนรหัสเสริมคำตอบอื่น ๆ ให้ความประทับใจกับฉันว่ามันจะช้าไม่ว่าฉันจะเปลี่ยนจากล่ามอ้างอิง - เพื่อล้างมัน OOP จะยังคง เป็นแนวทางที่เหมาะสมในมุมมองของคุณ?
lucasgcb

1
ด้วยกรณีที่ 1 (ย่อหน้าที่ 2) คุณไม่ได้รับเสียงที่เหมือนกันในการเรียกฟังก์ชั่นการโทรแม้ว่าจะมีการปฏิบัติตามฟังก์ชั่นเองหรือไม่?
Ewan

CPython เป็นล่ามตัวเดียวที่ใช้กันโดยทั่วไป PyPy น่าสนใจแต่แน่นอนว่าไม่ได้เห็นการยอมรับอย่างกว้างขวาง นอกจากนี้พฤติกรรมของมันแตกต่างจาก CPython และมันไม่สามารถใช้ได้กับแพ็คเกจที่สำคัญบางอย่างเช่น scipy นักพัฒนาที่มีสติน้อยจะแนะนำ PyPy สำหรับการผลิต ดังนั้นความแตกต่างระหว่างภาษาและการใช้งานจึงไม่สำคัญในทางปฏิบัติ
jpmc26

ฉันคิดว่าคุณโดนเล็บที่หัว ไม่มีเหตุผลที่คุณไม่มีล่ามที่ดีกว่าหรือแปลภาษา มันไม่ได้มีอยู่จริงในภาษาไพ ธ อน คุณติดอยู่กับความเป็นจริงที่ใช้งานได้จริง
Ewan

@ jpmc26 ฉันได้ใช้ PyPy ในการผลิตและแนะนำให้ลองทำกับนักพัฒนาที่มีประสบการณ์คนอื่น ๆ มันยอดเยี่ยมมากสำหรับ microservices ที่ใช้falconframework.orgสำหรับ API ที่มีน้ำหนักเบา (เป็นตัวอย่าง) พฤติกรรมที่แตกต่างกันเนื่องจากผู้พัฒนาใช้รายละเอียดของการติดตั้งซึ่งไม่ได้รับประกันถึงภาษาไม่ใช่เหตุผลที่จะไม่ใช้ PyPy เป็นเหตุผลในการเขียนรหัสของคุณใหม่ รหัสเดียวกันอาจผิดพลาดได้หาก CPython ทำการเปลี่ยนแปลงการปรับใช้ (ซึ่งมีอิสระที่จะทำตราบเท่าที่ยังคงเป็นไปตามข้อกำหนดภาษา)
Steven Jackson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.