Apa cara tercepat untuk menghitung dosa dan cos bersama-sama?

100

Saya ingin menghitung sinus dan co-sinus dari suatu nilai bersama-sama (misalnya untuk membuat matriks rotasi). Tentu saja saya dapat menghitungnya secara terpisah satu demi satu seperti a = cos(x); b = sin(x);, tetapi saya bertanya-tanya apakah ada cara yang lebih cepat ketika membutuhkan kedua nilai tersebut.

Edit: Untuk meringkas jawaban sejauh ini:

  • Vlad berkata, bahwa ada perintah asm yangFSINCOSmenghitung keduanya (dalam waktu yang hampir bersamaan dengan panggilan untukFSINsendiri)

  • Seperti yang diperhatikan Chi , pengoptimalan ini terkadang sudah dilakukan oleh compiler (saat menggunakan flag pengoptimalan).

  • caf menunjukkan, bahwa fungsisincosdansincosfmungkin tersedia dan dapat dipanggil langsung dengan hanya memasukkanmath.h

  • Pendekatan tanascius menggunakan tabel look-up dibahas kontroversial. (Namun di komputer saya dan dalam skenario benchmark, ini berjalan 3x lebih cepat daripadasincosdengan akurasi yang hampir sama untuk floating point 32-bit.)

  • Joel Goodwin ditautkan ke pendekatan menarik dari teknik pendekatan yang sangat cepat dengan akurasi yang cukup baik (bagi saya, ini bahkan lebih cepat daripada pencarian tabel)

Danvil
sumber
1
Lihat juga pertanyaan ini tentang implementasi asli sin / cos: stackoverflow.com/questions/1640595
Joel Goodwin
1
coba sinx ~ x-x^3/6dan cosx~1-x^2/4sebagai perkiraan jika Anda lebih memperhatikan kecepatan daripada akurasi. Anda dapat menambahkan istilah di salah satu rangkaian saat Anda memberi bobot lebih pada keakuratan ( en.wikipedia.org/wiki/Taylor_series gulir ke bawah ke deret trig taylor.) Perhatikan bahwa ini adalah cara umum untuk memperkirakan fungsi apa pun yang Anda inginkan yaitu nwaktu yang dapat dibedakan . Jadi jika Anda memiliki beberapa fungsi yang lebih besar dari sinus dan cosinus itu, Anda akan mendapatkan kecepatan yang jauh lebih besar jika Anda memperkirakannya alih-alih sin, cos secara independen.
Anjing
Ini adalah teknik yang buruk dengan akurasi yang sangat buruk. Lihat posting oleh Joel Goodwin. Seri Taylor telah diposting di bawah ini. Silakan posting sebagai jawaban.
Danvil
1
Baik itu tergantung pada kebutuhan Anda, jika Anda ingin akurasi deret Taylor akan menjadi perkiraan yang baik hanya jika Anda membutuhkan nilaix mendekati beberapa titik x_0, kemudian perluas deret Taylor Anda sekitar x_0bukannya 0. Ini akan memberi Anda akurasi yang sangat baik di dekat x_0tetapi semakin jauh Anda semakin buruk hasilnya. Anda mungkin mengira keakuratan menyebalkan karena Anda melihat jawaban yang diberikan dan mencobanya untuk nilai yang jauh dari 0. Jawabannya adalah dengan dosa, karena diperluas sekitar 0.
anjing

Jawaban:

52

Prosesor Intel / AMD modern memiliki instruksi FSINCOSuntuk menghitung fungsi sinus dan kosinus secara bersamaan. Jika Anda membutuhkan pengoptimalan yang kuat, mungkin Anda harus menggunakannya.

Berikut adalah contoh kecilnya: http://home.broadpark.no/~alein/fsincos.html

Berikut adalah contoh lain (untuk MSVC): http://www.codeguru.com/forum/showthread.php?t=328669

Berikut adalah contoh lain (dengan gcc): http://www.allegro.cc/forums/thread/588470

Semoga salah satu dari mereka membantu. (Saya tidak menggunakan instruksi ini sendiri, maaf.)

Karena mereka didukung pada tingkat prosesor, saya berharap mereka jauh lebih cepat daripada pencarian tabel.

Sunting:
Wikipedia menyarankan FSINCOSitu ditambahkan pada prosesor 387, sehingga Anda hampir tidak dapat menemukan prosesor yang tidak mendukungnya.

Edit:
Dokumentasi Intel menyatakan bahwa FSINCOShanya sekitar 5 kali lebih lambat daripada FDIV(yaitu, pembagian floating point).

Edit:
Perlu diketahui bahwa tidak semua penyusun modern mengoptimalkan kalkulasi sinus dan cosinus menjadi panggilan ke FSINCOS. Secara khusus, VS 2008 saya tidak melakukannya seperti itu.

Sunting:
Tautan contoh pertama sudah mati, tetapi masih ada versi di Mesin Wayback .

Vlad
sumber
1
@phkahler: Itu akan bagus. Tidak tahu apakah pengoptimalan seperti itu digunakan oleh kompiler modern.
Vlad
12
The fsincosinstruksi tidak "cukup cepat". Manual pengoptimalan Intel sendiri mengutipnya yang membutuhkan antara 119 dan 250 siklus pada arsitektur mikro terkini. Pustaka matematika Intel (didistribusikan dengan ICC), sebagai perbandingan, dapat menghitung secara terpisahsin dan cosdalam waktu kurang dari 100 siklus, menggunakan implementasi perangkat lunak yang menggunakan SSE, bukan unit x87. Implementasi perangkat lunak serupa yang menghitung keduanya secara bersamaan bisa lebih cepat lagi.
Stephen Canon
2
@Vlad: Perpustakaan matematika ICC bukan open-source, dan saya tidak memiliki lisensi untuk mendistribusikannya kembali, jadi saya tidak dapat memposting rakitan. Saya dapat memberitahu Anda bahwa tidak ada sinkomputasi built-in yang dapat mereka manfaatkan; mereka menggunakan instruksi SSE yang sama seperti orang lain. Untuk komentar kedua Anda, kecepatan relatif terhadap fdivtidak penting; jika ada dua cara untuk melakukan sesuatu dan yang satu dua kali lebih cepat dari yang lain, tidak masuk akal untuk menyebut yang lebih lambat "cepat", terlepas dari berapa lama waktu yang dibutuhkan relatif untuk beberapa tugas yang sama sekali tidak terkait.
Stephen Canon
1
Fungsi perangkat lunak sindi perpustakaan mereka memberikan akurasi presisi ganda penuh. The fsincosinstruksi memberikan agak lebih akurasi (double diperpanjang), tapi itu akurasi ekstra akan dibuang di sebagian besar program yang memanggil sinfungsi, sebagai hasilnya biasanya dibulatkan ke presisi ganda oleh operasi kemudian aritmatika atau toko ke memori. Dalam kebanyakan situasi, mereka memberikan akurasi yang sama untuk penggunaan praktis.
Stephen Canon
4
Perhatikan juga bahwa fsincositu bukanlah implementasi lengkap dengan sendirinya; Anda memerlukan langkah pengurangan jarak tambahan untuk meletakkan argumen ke dalam kisaran input yang valid untuk fsincosinstruksi. Pustaka sindan cosfungsi menyertakan pengurangan ini serta penghitungan inti, sehingga mereka bahkan lebih cepat (dengan perbandingan) daripada pengaturan waktu siklus yang mungkin saya tunjukkan.
Stephen Canon
39

Prosesor x86 modern memiliki instruksi fsincos yang akan melakukan apa yang Anda minta - hitung sin dan cos pada saat yang bersamaan. Kompiler pengoptimalan yang baik harus mendeteksi kode yang menghitung sin dan cos untuk nilai yang sama dan menggunakan perintah fsincos untuk menjalankannya.

Butuh beberapa twiddling dari flag compiler untuk bekerja, tapi:

$ gcc --version
i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5488)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cat main.c
#include <math.h> 

struct Sin_cos {double sin; double cos;};

struct Sin_cos fsincos(double val) {
  struct Sin_cos r;
  r.sin = sin(val);
  r.cos = cos(val);
  return r;
}

$ gcc -c -S -O3 -ffast-math -mfpmath=387 main.c -o main.s

$ cat main.s
    .text
    .align 4,0x90
.globl _fsincos
_fsincos:
    pushl   %ebp
    movl    %esp, %ebp
    fldl    12(%ebp)
    fsincos
    movl    8(%ebp), %eax
    fstpl   8(%eax)
    fstpl   (%eax)
    leave
    ret $4
    .subsections_via_symbols

Tada, ini menggunakan instruksi fsincos!

Chi
sumber
Ini keren! Bisakah Anda menjelaskan apa yang dilakukan -mfpmath = 387? Dan apakah itu juga bekerja dengan MSVC?
Danvil
1
Perhatikan itu -ffast-mathdan -mfpmathmengarah pada hasil yang berbeda dalam beberapa kasus.
Debilski
3
mfpmath = 387 akan memaksa gcc untuk menggunakan instruksi x87 alih-alih instruksi SSE. Saya menduga MSVC memiliki pengoptimalan dan flag yang serupa, tetapi saya tidak memiliki MSVC untuk memastikannya. Menggunakan instruksi x87 kemungkinan akan merusak kinerja dalam kode lain, Anda juga harus melihat jawaban saya yang lain, untuk menggunakan Intel MKL.
Chi
Gcc 3.4.4 lama saya dari cygwin menghasilkan 2 panggilan terpisah ke fsindan fcos. :-(
Vlad
Mencoba dengan Visual Studio 2008 dengan pengoptimalan tertinggi diaktifkan. Ini memanggil 2 fungsi perpustakaan __CIsindan __CIcos.
Vlad
13

Ketika Anda membutuhkan kinerja, Anda dapat menggunakan tabel sin / cos yang telah dihitung sebelumnya (satu tabel akan dilakukan, disimpan sebagai Kamus). Yah, itu tergantung pada akurasi yang Anda butuhkan (mungkin tabelnya akan terlalu besar), tetapi itu harus sangat cepat.

tanascius.dll
sumber
Kemudian nilai masukan perlu dipetakan ke [0,2 * pi] (atau lebih kecil dengan pemeriksaan tambahan) dan panggilan ke fmod ini menggerogoti kinerja. Dalam implementasi saya (mungkin suboptimal), saya tidak bisa mendapatkan kinerja dengan tabel pencarian. Apakah Anda punya saran di sini?
Danvil
11
Tabel yang telah dihitung sebelumnya hampir pasti akan lebih lambat daripada hanya memanggil sinkarena tabel yang dihitung sebelumnya akan membuang cache.
Andreas Brinck
1
Itu tergantung seberapa besar mejanya. Sebuah tabel 256 entri seringkali cukup akurat dan hanya menggunakan 1Kb ... jika Anda sering menggunakannya, bukankah itu akan macet di cache tanpa mempengaruhi kinerja aplikasi lainnya?
Tn. Boy
@Danvil: Berikut adalah contoh tabel pencarian sinus en.wikipedia.org/wiki/Lookup_table#Computing_sines . Namun ini mengasumsikan bahwa Anda telah memetakan input Anda ke [0; 2pi] juga.
tanascius
@AndreasBrinck Saya tidak akan pergi sejauh itu. Itu Tergantung (TM). Cache modern berukuran besar dan tabel pencarian berukuran kecil. Cukup sering jika Anda sedikit berhati-hati dalam tata letak memori, tabel pencarian Anda tidak perlu membuat perbedaan pada penggunaan cache dari sisa komputasi Anda. Fakta bahwa tabel pemeta cocok dengan cache adalah salah satu alasan mengapa begitu cepat. Bahkan di Java di mana sulit untuk mengontrol tata letak mem dengan tepat, saya mendapatkan kemenangan kinerja besar dengan tabel pencarian.
Jarrod Smith
13

Secara teknis, Anda akan mencapai ini dengan menggunakan bilangan kompleks dan Rumus Euler . Jadi, sesuatu seperti (C ++)

complex<double> res = exp(complex<double>(0, x));
// or equivalent
complex<double> res = polar<double>(1, x);
double sin_x = res.imag();
double cos_x = res.real();

harus memberi Anda sinus dan cosinus dalam satu langkah. Bagaimana ini dilakukan secara internal adalah pertanyaan tentang kompilator dan pustaka yang digunakan. Ini bisa (dan mungkin) membutuhkan waktu lebih lama untuk melakukannya dengan cara ini (hanya karena Formula Euler sebagian besar digunakan untuk menghitung kompleks expmenggunakan sindan cos- dan bukan sebaliknya) tetapi mungkin ada beberapa optimasi teoritis yang mungkin.


Edit

Header di <complex>untuk GNU C ++ 4.2 menggunakan kalkulasi eksplisit sindan cosdi dalamnya polar, sehingga tidak terlihat terlalu bagus untuk pengoptimalan di sana kecuali jika kompilator melakukan sihir (lihat -ffast-mathdan -mfpmathberalih seperti yang tertulis dalam jawaban Chi ).

Debilski
sumber
maaf, tetapi Rumus Euler tidak benar-benar memberi tahu Anda cara menghitung sesuatu, itu hanya identitas (meskipun sangat berguna) yang menghubungkan eksponensial kompleks dengan fungsi trigonometri nyata. Ada keuntungan dari menghitung sinus dan kosinus bersama-sama, tetapi melibatkan subekspresi umum dan jawaban Anda tidak membahas hal ini.
Jason S
12

Anda dapat menghitung salah satunya dan kemudian menggunakan identitas:

cos (x) 2 = 1 - sin (x) 2

tetapi seperti yang dikatakan @tanascius, tabel yang telah dihitung sebelumnya adalah cara yang tepat.

Mitch Wheat
sumber
8
Dan ketahuilah bahwa menggunakan metode ini melibatkan penghitungan pangkat dan akar kuadrat, jadi jika kinerja itu penting, pastikan untuk memverifikasi bahwa ini sebenarnya lebih cepat daripada menghitung fungsi trigonometri lainnya secara langsung.
Tyler McHenry
4
sqrt()sering dioptimalkan dalam perangkat keras, jadi mungkin lebih cepat saat itu sin()atau cos(). Kekuatannya hanyalah perkalian diri, jadi jangan gunakan pow(). Ada beberapa trik untuk mendapatkan akar kuadrat yang cukup akurat dengan sangat cepat tanpa dukungan perangkat keras. Terakhir, pastikan untuk membuat profil sebelum melakukan semua ini.
deft_code
12
Perhatikan bahwa √ (1 - cos ^ 2 x) kurang akurat daripada menghitung sin x secara langsung, khususnya ketika x ~ 0.
kennytm
1
Untuk x kecil, deret Taylor untuk y = akar persegi (1-x * x) sangat bagus. Anda bisa mendapatkan akurasi yang baik dengan 3 suku pertama dan hanya membutuhkan beberapa kali perkalian dan satu shift. Saya telah menggunakannya dalam kode titik tetap.
phkahler
1
@phkahler: Seri Taylor Anda tidak berlaku karena ketika x ~ 0, cos x ~ 1.
kennytm
10

Jika Anda menggunakan perpustakaan GNU C, maka Anda dapat melakukan:

#define _GNU_SOURCE
#include <math.h>

dan Anda akan mendapatkan deklarasi sincos(), sincosf()dan sincosl()fungsi yang menghitung kedua nilai bersama - mungkin dengan cara tercepat untuk arsitektur target Anda.

kafe
sumber
8

Ada hal yang sangat menarik di halaman forum ini, yang difokuskan untuk menemukan perkiraan yang baik dan cepat: http://www.devmaster.net/forums/showthread.php?t=5784

Penafian: Saya sendiri tidak menggunakan barang ini.

Pembaruan 22 Feb 2018: Wayback Machine adalah satu-satunya cara untuk mengunjungi halaman asli sekarang: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- sinus-kosinus

Joel Goodwin
sumber
Saya mencoba yang ini juga, dan itu memberi saya kinerja yang cukup bagus. Tapi sin dan cos dihitung secara independen.
Danvil
Perasaan saya adalah perhitungan sinus / kosinus ini akan lebih cepat daripada mendapatkan sinus dan menggunakan pendekatan akar kuadrat untuk mendapatkan kosinus, tetapi tes akan memverifikasi itu. Hubungan utama antara sinus dan kosinus adalah salah satu fase; apakah mungkin untuk membuat kode sehingga Anda dapat menggunakan kembali nilai sinus yang Anda hitung untuk panggilan kosinus bergeser fase dengan mempertimbangkan ini? (Ini mungkin berlebihan, tetapi harus bertanya)
Joel Goodwin
Tidak secara langsung (meskipun ada pertanyaan yang menanyakan hal ini). Saya membutuhkan sin dan cos dari nilai x dan tidak ada cara untuk mengetahui apakah di tempat lain saya secara kebetulan menghitung x + pi / 2 ...
Danvil
Saya menggunakannya dalam permainan saya untuk menggambar lingkaran partikel. Karena ini hanya efek visual, hasilnya cukup mendekati, dan performanya sangat mengesankan.
Maxim Kamalov
Aku tidak terkesan; Perkiraan Chebyshev biasanya memberi Anda akurasi paling tinggi untuk performa tertentu.
Jason S
7

Banyak perpustakaan matematika C, seperti yang ditunjukkan oleh caf, sudah memiliki sincos (). Pengecualian penting adalah MSVC.

  • Sun telah memiliki sincos () setidaknya sejak 1987 (dua puluh tiga tahun; saya memiliki halaman manual hard copy)
  • HPUX 11 memilikinya pada tahun 1997 (tetapi tidak ada di HPUX 10.20)
  • Ditambahkan ke glibc pada versi 2.1 (Feb 1999)
  • Menjadi bawaan di gcc 3.4 (2004), __builtin_sincos ().

Dan mengenai pencarian, Eric S.Raymond dalam Art of Unix Programming (2004) (Bab 12) secara eksplisit mengatakan ini Ide Buruk (pada saat ini dalam waktu):

"Contoh lain adalah prakomputasi tabel kecil - misalnya, tabel sin (x) menurut derajat untuk mengoptimalkan rotasi dalam mesin grafis 3D akan membutuhkan 365 × 4 byte pada mesin modern. Sebelumnya prosesor mendapatkan cukup waktu lebih cepat daripada memori untuk menuntut penyimpanan dalam cache , ini jelas merupakan pengoptimalan kecepatan. Saat ini, mungkin lebih cepat untuk menghitung ulang setiap kali daripada membayar persentase cache tambahan yang hilang yang disebabkan oleh tabel.

"Namun di masa mendatang, ini mungkin berbalik lagi karena cache tumbuh lebih besar. Secara lebih umum, banyak pengoptimalan bersifat sementara dan dapat dengan mudah berubah menjadi pesimisasi saat rasio biaya berubah. Satu-satunya cara untuk mengetahuinya adalah dengan mengukur dan melihat." (dari Seni Pemrograman Unix )

Tapi, dilihat dari pembahasan di atas, tidak semua orang setuju.

Joseph Quinsey
sumber
10
"365 x 4 byte". Anda perlu memperhitungkan tahun kabisat, jadi seharusnya berukuran 365,25 x 4 byte. Atau mungkin dia bermaksud menggunakan angka derajat dalam lingkaran daripada jumlah hari dalam satu tahun bumi.
Ponkadoodle
@ Wallacoloo: Pengamatan yang bagus. Saya melewatkannya. Tapi kesalahannya ada pada aslinya .
Joseph Quinsey
LOL. Plus, dia mengabaikan fakta bahwa di banyak game komputer di area itu, Anda hanya membutuhkan jumlah sudut yang terbatas. Tidak ada cache yang terlewat, jika Anda mengetahui sudut yang mungkin. Saya akan menggunakan tabel persis dalam kasus ini, dan memberikan fsincos(instruksi CPU!) Mencoba untuk yang lain. Seringkali secepat menginterpolasi dosa dan cos dari tabel besar.
Erich Schubert
5

Saya tidak percaya bahwa tabel pencarian merupakan ide bagus untuk masalah ini. Kecuali jika persyaratan akurasi Anda sangat rendah, tabel harus berukuran sangat besar. Dan CPU modern dapat melakukan banyak komputasi saat sebuah nilai diambil dari memori utama. Ini bukan salah satu pertanyaan yang dapat dijawab dengan benar dengan argumen (bahkan bukan milik saya), menguji dan mengukur dan mempertimbangkan data.

Tapi saya akan melihat implementasi cepat SinCos yang Anda temukan di perpustakaan seperti ACML AMD dan MKL Intel.

Tanda Kinerja Tinggi
sumber
3

Jika Anda ingin menggunakan produk komersial, dan menghitung jumlah penghitungan sin / cos pada saat yang sama (sehingga Anda dapat menggunakan fungsi vektor), Anda harus memeriksa Pustaka Kernel Matematika Intel.

Ini memiliki fungsi sincos

Menurut dokumentasi itu, rata-rata 13.08 jam / elemen pada inti 2 duo dalam mode akurasi tinggi, yang menurut saya akan lebih cepat daripada fsincos.

Chi
sumber
1
Demikian pula, di OSX seseorang dapat menggunakan vvsincosatau vvsincosfdari Accelerate.framework. Saya percaya bahwa AMD memiliki fungsi serupa di perpustakaan vektor mereka juga.
Stephen Canon
2

Ketika kinerja sangat penting untuk hal semacam ini, bukan hal yang aneh untuk memperkenalkan tabel pencarian.

Tom Cabanski
sumber
2

Untuk pendekatan kreatif, bagaimana dengan memperluas seri Taylor? Karena mereka memiliki istilah yang mirip, Anda dapat melakukan sesuatu seperti berikut ini:

numerator = x
denominator = 1
sine = x
cosine = 1
op = -1
fact = 1

while (not enough precision) {
    fact++
    denominator *= fact
    numerator *= x

    cosine += op * numerator / denominator

    fact++
    denominator *= fact
    numerator *= x

    sine += op * numerator / denominator

    op *= -1
}

Ini berarti Anda melakukan sesuatu seperti ini: mulai dari x dan 1 untuk sin dan cosinus, ikuti polanya - kurangi x ^ 2/2! dari cosinus, kurangi x ^ 3/3! dari sinus, tambahkan x ^ 4/4! ke kosinus, tambahkan x ^ 5/5! untuk sinus ...

Saya tidak tahu apakah ini akan menjadi performant. Jika Anda membutuhkan presisi yang kurang dari yang diberikan oleh sin () dan cos (), ini bisa menjadi pilihan.

Tesserex
sumber
Sebenarnya faktor ekstensi sinus ke-i adalah x / i kali faktor ekstensi kosinus ke-i. Tapi saya ragu bahwa menggunakan seri Taylor sangat cepat ...
Danvil
1
Chebyshev jauh lebih baik daripada Taylor untuk pendekatan fungsi polinomial. Jangan gunakan pendekatan Taylor.
Timmmm
Ada banyak kecerobohan numerik di sini; pembilang dan penyebut keduanya dengan cepat menjadi besar dan menyebabkan kesalahan floating-point. Belum lagi bagaimana Anda memutuskan apa yang "tidak cukup presisi" dan bagaimana cara menghitungnya? Perkiraan Taylor baik di lingkungan sekitar satu titik; menjauh dari titik itu mereka dengan cepat menjadi tidak akurat dan membutuhkan sejumlah besar istilah, itulah sebabnya saran Timmmm tentang pendekatan Chebyshev (yang menciptakan perkiraan yang baik selama interval tertentu) adalah bagus.
Jason S
2

Ada solusi bagus di pustaka CEPHES yang bisa sangat cepat dan Anda dapat menambah / menghapus akurasi dengan cukup fleksibel untuk waktu CPU yang lebih banyak / lebih sedikit.

Ingatlah bahwa cos (x) dan sin (x) adalah bagian nyata dan imajiner dari exp (ix). Jadi kami ingin menghitung exp (ix) untuk mendapatkan keduanya. Kami menghitung sebelumnya exp (iy) untuk beberapa nilai diskrit y antara 0 dan 2pi. Kami menggeser x ke interval [0, 2pi). Kemudian kami memilih y yang paling dekat dengan x dan menulis
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).

Kami mendapatkan exp (iy) dari tabel pencarian. Dan sejak | xy | kecil (paling banyak setengah jarak antara nilai y), deret Taylor akan bertemu dengan baik hanya dalam beberapa suku, jadi kami menggunakannya untuk exp (i (xy)). Dan kemudian kita hanya perlu perkalian kompleks untuk mendapatkan exp (ix).

Properti bagus lainnya dari ini adalah Anda dapat melakukan vektorisasi menggunakan SSE.

Jsl
sumber
2

Anda mungkin ingin melihat http://gruntthepeon.free.fr/ssemath/ , yang menawarkan implementasi vektorisasi SSE yang terinspirasi dari pustaka CEPHES. Ini memiliki akurasi yang baik (deviasi maksimum dari sin / cos pada urutan 5e-8) dan kecepatan (sedikit mengungguli fsincos pada basis panggilan tunggal, dan pemenang yang jelas atas beberapa nilai).

SleuthEye
sumber
1

Saya telah memposting solusi yang melibatkan perakitan ARM inline yang mampu menghitung sinus dan cosinus dari dua sudut sekaligus di sini: Sinus / cosinus cepat untuk ARMv7 + NEON

jcayzac.dll
sumber
0

Pernahkah Anda berpikir untuk mendeklarasikan tabel pencarian untuk dua fungsi? Anda masih harus "menghitung" sin (x) dan cos (x), tetapi ini akan menjadi lebih cepat, jika Anda tidak membutuhkan tingkat akurasi yang tinggi.

Frank Shearar
sumber
0

Kompilator MSVC dapat menggunakan fungsi SSE2 (internal)

 ___libm_sse2_sincos_ (for x86)
 __libm_sse2_sincos_  (for x64)

dalam build yang dioptimalkan jika flag compiler yang sesuai ditentukan (minimal / O2 / arch: SSE2 / fp: fast). Nama-nama fungsi ini sepertinya menyiratkan bahwa mereka tidak menghitung sin dan cos yang terpisah, tetapi keduanya "dalam satu langkah".

Sebagai contoh:

void sincos(double const x, double & s, double & c)
{
  s = std::sin(x);
  c = std::cos(x);
}

Majelis (untuk x86) dengan / fp: fast:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    ___libm_sse2_sincos_
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
mov     eax, DWORD PTR _c$[esp-4]
shufpd  xmm0, xmm0, 1
movsd   QWORD PTR [eax], xmm0
ret     0

Majelis (untuk x86) tanpa / fp: cepat tetapi dengan / fp: tepat sebagai gantinya (yang merupakan default) memanggil sin dan cos terpisah:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_sin_precise
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_cos_precise
mov     eax, DWORD PTR _c$[esp-4]
movsd   QWORD PTR [eax], xmm0
ret     0

So / fp: fast adalah wajib untuk optimasi sincos.

Tapi harap dicatat itu

___libm_sse2_sincos_

mungkin tidak seakurat

__libm_sse2_sin_precise
__libm_sse2_cos_precise

karena "tepat" yang hilang di akhir namanya.

Pada sistem saya yang "sedikit" lebih tua (Intel Core 2 Duo E6750) dengan kompiler MSVC 2019 terbaru dan pengoptimalan yang sesuai, tolok ukur saya menunjukkan bahwa panggilan sincos sekitar 2,4 kali lebih cepat daripada panggilan sin dan cos yang terpisah.

xy
sumber