* কলিং * = (বা * = কলিং *) পৃথক ফাংশনগুলি (গণিতের গ্রন্থাগারের জন্য) লেখার চেয়ে ধীর? [বন্ধ]


15

আমার কয়েকটি ভেক্টর ক্লাস রয়েছে যেখানে পাটিগণিতের ফাংশনগুলি দেখতে এরকম দেখাচ্ছে:

template<typename T, typename U>
auto operator*(const Vector3<T>& lhs, const Vector3<U>& rhs)
{
    return Vector3<decltype(lhs.x*rhs.x)>(
        lhs.x + rhs.x,
        lhs.y + rhs.y,
        lhs.z + rhs.z
        );
}

template<typename T, typename U>
Vector3<T>& operator*=(Vector3<T>& lhs, const Vector3<U>& rhs)
{
    lhs.x *= rhs.x;
    lhs.y *= rhs.y;
    lhs.z *= rhs.z;

    return lhs;
}

সদৃশ কোডটি মুছে ফেলতে আমি কিছুটা পরিষ্কার করতে চাই। মূলত, আমি সমস্ত ফাংশনগুলিকে এই জাতীয় কলগুলিতে রূপান্তর operator*করতে operator*=চাই:

template<typename T, typename U>
auto operator*(const Vector3<T>& lhs, const Vector3<U>& rhs)
{
    Vector3<decltype(lhs.x*rhs.x)> result = lhs;
    result *= rhs;
    return result;
}

তবে এটি অতিরিক্ত ফাংশন কল থেকে কোনও অতিরিক্ত ওভারহেড ব্যয় করবে কিনা তা নিয়ে আমি উদ্বিগ্ন।

এটা কি ভাল ধারণা? খারাপ ধারণা?


2
এটি সংকলক থেকে সংকলক থেকে আলাদা হতে পারে। আপনি নিজে চেষ্টা করেছেন? সেই অপারেশনটি ব্যবহার করে একটি সংক্ষিপ্ত প্রোগ্রাম লিখুন। তারপরে ফলাফলের সমাবেশ কোডটি তুলনা করুন।
মারিও

1
আহ, আমি প্রচুর সি / সি ++ জানি না তবে ... দেখে মনে হচ্ছে *এবং *=দুটি আলাদা জিনিস করছে - প্রাক্তন পৃথক মানগুলিকে যুক্ত করে, পরবর্তীগুলি তাদের গুণ করে। তাদের বিভিন্ন ধরণের স্বাক্ষর রয়েছে বলে মনে হয়।
ক্লকওয়ার্ক-যাদুঘর

3
এটিকে খাঁটি সি ++ প্রোগ্রামিং প্রশ্নের মতো মনে হচ্ছে গেম বিকাশের সাথে সুনির্দিষ্ট কিছু নেই। সম্ভবত এটি স্ট্যাক ওভারফ্লোতে স্থানান্তরিত করা উচিত ?
ইলমারি করোনেন

আপনি যদি পারফরম্যান্স সম্পর্কে চিন্তিত হন তবে আপনার সিমডের নির্দেশাবলীর দিকে নজর দেওয়া উচিত: en.wikedia.org/wiki/Streaming_SIMD_Exitions
পিটার

1
কমপক্ষে দুটি কারণে আপনার নিজের গণিত গ্রন্থাগারটি লিখবেন না। প্রথমত, আপনি সম্ভবত এসএসই অভ্যন্তরীণ বিশেষজ্ঞ নন, সুতরাং এটি দ্রুত হবে না। দ্বিতীয়ত, বীজগণিত গণনার জন্য জিপিইউ ব্যবহার করা অনেক বেশি দক্ষ কারণ এটি কেবল তার জন্য তৈরি। ডানদিকে "সম্পর্কিত" বিভাগটি দেখুন: গেমদেব.স্ট্যাকেক্সেঞ্জার
polkovnikov.ph

উত্তর:


18

অনুশীলনে, কোনও অতিরিক্ত ওভারহেড ব্যয় করা হবে না । সি ++ এ, ছোট ফাংশনগুলি সাধারণত সংযোজনকারী হিসাবে একটি অপ্টিমাইজেশন হিসাবে অন্তর্ভুক্ত থাকে, ফলে ফলস্বরূপ অ্যাসেমব্লিং কলসিটে সমস্ত ক্রিয়াকলাপ করবে - ফাংশনগুলি একে অপরকে কল করবে না, যেহেতু ফাংশনগুলি চূড়ান্ত কোডে থাকবে না, কেবলমাত্র গাণিতিক ক্রিয়াকলাপ।

সংকলকটির উপর নির্ভর করে আপনি দেখতে পাচ্ছেন যে এই ফাংশনগুলির মধ্যে একটি অপরকে কম বা কম অপ্টিমাইজেশান (ডিবাগ বিল্ডগুলির মতো) দিয়ে কল করছে। উচ্চতর অপ্টিমাইজেশন স্তরে যদিও (রিলিজ বিল্ডগুলি), সেগুলি কেবলমাত্র গণিতের জন্য অনুকূলিত হবে।

আপনি যদি এখনও এটি সম্পর্কে পেডেন্টিক হতে চান (বলুন আপনি একটি লাইব্রেরি তৈরি করছেন), (এবং অনুরূপ মোড়ক ফাংশনগুলির) inlineকীওয়ার্ডটি operator*()ইনলাইন সম্পাদন করতে আপনার সংকলককে ইঙ্গিত করতে পারে, বা সংকলক-নির্দিষ্ট পতাকা / বাক্য গঠন যেমন: -finline-small-functions, -finline-functions, -findirect-inlining, __attribute__((always_inline)) (মন্তব্যে @Stephane Hockenhull এর সহায়ক তথ্য কৃতিত্ব) । ব্যক্তিগতভাবে, আমি যে ফ্রেমওয়ার্ক / লিবগুলি ব্যবহার করছি তা অনুসরণ করার ঝোঁক I'm আমি যদি জিএলকিটের গণিত লাইব্রেরি ব্যবহার করছি, আমি কেবল GLK_INLINEএটি সরবরাহকারী ম্যাক্রোটি ব্যবহার করব ।


ক্ল্যাং (এক্সকোড 7.2 এর অ্যাপল এলএলভিএম সংস্করণ 7.0.2 / ক্ল্যাং-700.1.81) ব্যবহার করে ডাবল চেকিং , নিম্নলিখিত main()ক্রিয়াকলাপটি (আপনার ফাংশন এবং একটি নির্বিকার Vector3<T>প্রয়োগের সাথে মিলিয়ে ):

int main(int argc, const char * argv[])
{
    Vector3<int> a = { 1, 2, 3 };
    Vector3<int> b;
    scanf("%d", &b.x);
    scanf("%d", &b.y);
    scanf("%d", &b.z);

    Vector3<int> c = a * b;

    printf("%d, %d, %d\n", c.x, c.y, c.z);

    return 0;
}

অপ্টিমাইজেশন পতাকা ব্যবহার করে এই সমাবেশে সংকলন করুন -O0:

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
Lfunc_begin0:
    .loc    6 30 0                  ## main.cpp:30:0
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    subq    $128, %rsp
    leaq    L_.str1(%rip), %rax
    ##DEBUG_VALUE: main:argc <- undef
    ##DEBUG_VALUE: main:argv <- undef
    movl    $0, -4(%rbp)
    movl    %edi, -8(%rbp)
    movq    %rsi, -16(%rbp)
    .loc    6 31 15 prologue_end    ## main.cpp:31:15
Ltmp3:
    movl    l__ZZ4mainE1a+8(%rip), %edi
    movl    %edi, -24(%rbp)
    movq    l__ZZ4mainE1a(%rip), %rsi
    movq    %rsi, -32(%rbp)
    .loc    6 33 2                  ## main.cpp:33:2
    leaq    L_.str(%rip), %rsi
    xorl    %edi, %edi
    movb    %dil, %cl
    leaq    -48(%rbp), %rdx
    movq    %rsi, %rdi
    movq    %rsi, -88(%rbp)         ## 8-byte Spill
    movq    %rdx, %rsi
    movq    %rax, -96(%rbp)         ## 8-byte Spill
    movb    %cl, %al
    movb    %cl, -97(%rbp)          ## 1-byte Spill
    movq    %rdx, -112(%rbp)        ## 8-byte Spill
    callq   _scanf
    .loc    6 34 17                 ## main.cpp:34:17
    leaq    -44(%rbp), %rsi
    .loc    6 34 2 is_stmt 0        ## main.cpp:34:2
    movq    -88(%rbp), %rdi         ## 8-byte Reload
    movb    -97(%rbp), %cl          ## 1-byte Reload
    movl    %eax, -116(%rbp)        ## 4-byte Spill
    movb    %cl, %al
    callq   _scanf
    .loc    6 35 17 is_stmt 1       ## main.cpp:35:17
    leaq    -40(%rbp), %rsi
    .loc    6 35 2 is_stmt 0        ## main.cpp:35:2
    movq    -88(%rbp), %rdi         ## 8-byte Reload
    movb    -97(%rbp), %cl          ## 1-byte Reload
    movl    %eax, -120(%rbp)        ## 4-byte Spill
    movb    %cl, %al
    callq   _scanf
    leaq    -32(%rbp), %rdi
    .loc    6 37 21 is_stmt 1       ## main.cpp:37:21
    movq    -112(%rbp), %rsi        ## 8-byte Reload
    movl    %eax, -124(%rbp)        ## 4-byte Spill
    callq   __ZmlIiiE7Vector3IDTmldtfp_1xdtfp0_1xEERKS0_IT_ERKS0_IT0_E
    movl    %edx, -72(%rbp)
    movq    %rax, -80(%rbp)
    movq    -80(%rbp), %rax
    movq    %rax, -64(%rbp)
    movl    -72(%rbp), %edx
    movl    %edx, -56(%rbp)
    .loc    6 39 27                 ## main.cpp:39:27
    movl    -64(%rbp), %esi
    .loc    6 39 32 is_stmt 0       ## main.cpp:39:32
    movl    -60(%rbp), %edx
    .loc    6 39 37                 ## main.cpp:39:37
    movl    -56(%rbp), %ecx
    .loc    6 39 2                  ## main.cpp:39:2
    movq    -96(%rbp), %rdi         ## 8-byte Reload
    movb    $0, %al
    callq   _printf
    xorl    %ecx, %ecx
    .loc    6 41 5 is_stmt 1        ## main.cpp:41:5
    movl    %eax, -128(%rbp)        ## 4-byte Spill
    movl    %ecx, %eax
    addq    $128, %rsp
    popq    %rbp
    retq
Ltmp4:
Lfunc_end0:
    .cfi_endproc

উপরের দিকে, __ZmlIiiE7Vector3IDTmldtfp_1xdtfp0_1xEERKS0_IT_ERKS0_IT0_Eআপনার operator*()ফাংশনটি হয় এবং callqঅন্য __…Vector3…ফাংশনটি শেষ করে । এটি যথেষ্ট সমাবেশ সমাবেশ। সঙ্গে সংকলন -O1প্রায় একই, এখনও __…Vector3…ফাংশন কল ।

যাইহোক, যখন আমরা এটা আপ আচমকা -O2, callqগুলি __…Vector3…অদৃশ্য, একটি দিয়ে প্রতিস্থাপিত imullনির্দেশ ( * a.z* 3), একটি addlনির্দেশ ( * a.y* 2), এবং মাত্র ব্যবহার b.xমান সোজা-আপ (কারণ * a.x* 1)।

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
Lfunc_begin0:
    .loc    6 30 0                  ## main.cpp:30:0
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    .loc    6 33 2 prologue_end     ## main.cpp:33:2
Ltmp3:
    pushq   %rbx
    subq    $24, %rsp
Ltmp4:
    .cfi_offset %rbx, -24
    ##DEBUG_VALUE: main:argc <- EDI
    ##DEBUG_VALUE: main:argv <- RSI
    leaq    L_.str(%rip), %rbx
    leaq    -24(%rbp), %rsi
Ltmp5:
    ##DEBUG_VALUE: operator*=<int, int>:rhs <- [RSI+0]
    ##DEBUG_VALUE: operator*<int, int>:rhs <- [RSI+0]
    ##DEBUG_VALUE: main:b <- [RSI+0]
    xorl    %eax, %eax
    movq    %rbx, %rdi
Ltmp6:
    callq   _scanf
    .loc    6 34 17                 ## main.cpp:34:17
    leaq    -20(%rbp), %rsi
Ltmp7:
    xorl    %eax, %eax
    .loc    6 34 2 is_stmt 0        ## main.cpp:34:2
    movq    %rbx, %rdi
    callq   _scanf
    .loc    6 35 17 is_stmt 1       ## main.cpp:35:17
    leaq    -16(%rbp), %rsi
    xorl    %eax, %eax
    .loc    6 35 2 is_stmt 0        ## main.cpp:35:2
    movq    %rbx, %rdi
    callq   _scanf
    .loc    6 22 18 is_stmt 1       ## main.cpp:22:18
Ltmp8:
    movl    -24(%rbp), %esi
    .loc    6 23 18                 ## main.cpp:23:18
    movl    -20(%rbp), %edx
    .loc    6 23 11 is_stmt 0       ## main.cpp:23:11
    addl    %edx, %edx
    .loc    6 24 11 is_stmt 1       ## main.cpp:24:11
    imull   $3, -16(%rbp), %ecx
Ltmp9:
    ##DEBUG_VALUE: main:c [bit_piece offset=64 size=32] <- ECX
    .loc    6 39 2                  ## main.cpp:39:2
    leaq    L_.str1(%rip), %rdi
    xorl    %eax, %eax
    callq   _printf
    xorl    %eax, %eax
    .loc    6 41 5                  ## main.cpp:41:5
    addq    $24, %rsp
    popq    %rbx
    popq    %rbp
    retq
Ltmp10:
Lfunc_end0:
    .cfi_endproc

এই কোডটি, সমাবেশ এ -O2, -O3, -Os, & -Ofastসব বর্ণন অভিন্ন।


হুম। আমি এখানে স্মৃতি থেকে দূরে চলেছি, তবে আমি মনে করি যে তারা সবসময় ভাষার নকশায় অন্তর্ভুক্ত হতে চায় এবং কেবলমাত্র ডিবাগিংয়ের জন্য অ-অনুকূলিত বিন্যাসগুলিতে অন্তর্নিহিত থাকে। হয়তো আমি অতীতে ব্যবহৃত একটি নির্দিষ্ট সংকলক সম্পর্কে ভাবছি।
স্লিপ ডি থমসন

@ পিটার উইকিপিডিয়া আপনার সাথে একমত বলে মনে হচ্ছে। Ugg। হ্যাঁ, আমি মনে করি আমি একটি নির্দিষ্ট সরঞ্জামচেনের কথা স্মরণ করছি। একটি ভাল উত্তর পোস্ট করুন দয়া করে?
স্লিপ ডি থম্পসন

@ পিটার রাইট আমার ধারণা আমি প্রলুব্ধকর দিকটিতে ধরা পড়েছিলাম। চিয়ার্স!
স্লিপ ডি থম্পসন

আপনি যদি টেমপ্লেট ফাংশনগুলিতে ইনলাইন কীওয়ার্ডটি সংযুক্ত করেন তবে সংযোজনকারীরা অপ্টিমাইজেশনের প্রথম স্তরে (-O1) ইনলাইন হওয়ার সম্ভাবনা বেশি। জিসিসির ক্ষেত্রে আপনি -ফিনলাইন-ছোট-ফাংশন -ফাইনলাইন-ফাংশন -ফাইন্ডাইর্ট-ইনলাইনিংয়ের সাহায্যে -O0 এ ইনলাইনিং সক্ষম করতে পারেন বা পোর্টেবল অল'ইনলাইন অ্যাট্রিবিউট ( inline void foo (const char) __attribute__((always_inline));) ব্যবহার করতে পারেন । আপনি যদি ভেক্টর-ভারী জিনিসগুলি ডিবাজেবলের সময়ে যুক্তিসঙ্গত গতিতে চালিত করতে চান।
স্টিফেন হোকেনহুল

1
এটি কেবলমাত্র একক গুণগত নির্দেশ উত্পন্ন করার কারণটি আপনি যে ধ্রুবক দ্বারা গুণ করছেন তার নিচে। 1 দ্বারা গুণিত করা কিছুই করে না, এবং 2 দ্বারা গুণিতটি অনুকূলীকৃত হয় addl %edx, %edx(অর্থাত্ মানটি নিজের মধ্যে যোগ করুন)।
আদম
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.