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 yang
FSINCOS
menghitung keduanya (dalam waktu yang hampir bersamaan dengan panggilan untukFSIN
sendiri)Seperti yang diperhatikan Chi , pengoptimalan ini terkadang sudah dilakukan oleh compiler (saat menggunakan flag pengoptimalan).
caf menunjukkan, bahwa fungsi
sincos
dansincosf
mungkin 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 daripada
sincos
dengan 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)
sinx ~ x-x^3/6
dancosx~1-x^2/4
sebagai 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 yaitun
waktu 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.x
mendekati beberapa titikx_0
, kemudian perluas deret Taylor Anda sekitarx_0
bukannya 0. Ini akan memberi Anda akurasi yang sangat baik di dekatx_0
tetapi semakin jauh Anda semakin buruk hasilnya. Anda mungkin mengira keakuratan menyebalkan karena Anda melihat jawaban yang diberikan dan mencobanya untuk nilai yang jauh dari0
. Jawabannya adalah dengan dosa, karena diperluas sekitar 0.Jawaban:
Prosesor Intel / AMD modern memiliki instruksi
FSINCOS
untuk 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
FSINCOS
itu ditambahkan pada prosesor 387, sehingga Anda hampir tidak dapat menemukan prosesor yang tidak mendukungnya.Edit:
Dokumentasi Intel menyatakan bahwa
FSINCOS
hanya sekitar 5 kali lebih lambat daripadaFDIV
(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 .
sumber
fsincos
instruksi 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
dancos
dalam 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.sin
komputasi built-in yang dapat mereka manfaatkan; mereka menggunakan instruksi SSE yang sama seperti orang lain. Untuk komentar kedua Anda, kecepatan relatif terhadapfdiv
tidak 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.sin
di perpustakaan mereka memberikan akurasi presisi ganda penuh. Thefsincos
instruksi memberikan agak lebih akurasi (double diperpanjang), tapi itu akurasi ekstra akan dibuang di sebagian besar program yang memanggilsin
fungsi, 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.fsincos
itu bukanlah implementasi lengkap dengan sendirinya; Anda memerlukan langkah pengurangan jarak tambahan untuk meletakkan argumen ke dalam kisaran input yang valid untukfsincos
instruksi. Pustakasin
dancos
fungsi menyertakan pengurangan ini serta penghitungan inti, sehingga mereka bahkan lebih cepat (dengan perbandingan) daripada pengaturan waktu siklus yang mungkin saya tunjukkan.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:
Tada, ini menggunakan instruksi fsincos!
sumber
-ffast-math
dan-mfpmath
mengarah pada hasil yang berbeda dalam beberapa kasus.fsin
danfcos
. :-(__CIsin
dan__CIcos
.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.
sumber
sin
karena tabel yang dihitung sebelumnya akan membuang cache.Secara teknis, Anda akan mencapai ini dengan menggunakan bilangan kompleks dan Rumus Euler . Jadi, sesuatu seperti (C ++)
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
exp
menggunakansin
dancos
- dan bukan sebaliknya) tetapi mungkin ada beberapa optimasi teoritis yang mungkin.Edit
Header di
<complex>
untuk GNU C ++ 4.2 menggunakan kalkulasi eksplisitsin
dancos
di dalamnyapolar
, sehingga tidak terlihat terlalu bagus untuk pengoptimalan di sana kecuali jika kompilator melakukan sihir (lihat-ffast-math
dan-mfpmath
beralih seperti yang tertulis dalam jawaban Chi ).sumber
Anda dapat menghitung salah satunya dan kemudian menggunakan identitas:
tetapi seperti yang dikatakan @tanascius, tabel yang telah dihitung sebelumnya adalah cara yang tepat.
sumber
sqrt()
sering dioptimalkan dalam perangkat keras, jadi mungkin lebih cepat saat itusin()
ataucos()
. Kekuatannya hanyalah perkalian diri, jadi jangan gunakanpow()
. 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.Jika Anda menggunakan perpustakaan GNU C, maka Anda dapat melakukan:
dan Anda akan mendapatkan deklarasi
sincos()
,sincosf()
dansincosl()
fungsi yang menghitung kedua nilai bersama - mungkin dengan cara tercepat untuk arsitektur target Anda.sumber
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
sumber
Banyak perpustakaan matematika C, seperti yang ditunjukkan oleh caf, sudah memiliki sincos (). Pengecualian penting adalah MSVC.
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):
Tapi, dilihat dari pembahasan di atas, tidak semua orang setuju.
sumber
fsincos
(instruksi CPU!) Mencoba untuk yang lain. Seringkali secepat menginterpolasi dosa dan cos dari tabel besar.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.
sumber
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.
sumber
vvsincos
atauvvsincosf
dari Accelerate.framework. Saya percaya bahwa AMD memiliki fungsi serupa di perpustakaan vektor mereka juga.Artikel ini menunjukkan cara membangun algoritme parabola yang menghasilkan sinus dan kosinus:
Trik DSP: Pendekatan Parabola Simultan dari Sin dan Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
sumber
Ketika kinerja sangat penting untuk hal semacam ini, bukan hal yang aneh untuk memperkenalkan tabel pencarian.
sumber
Untuk pendekatan kreatif, bagaimana dengan memperluas seri Taylor? Karena mereka memiliki istilah yang mirip, Anda dapat melakukan sesuatu seperti berikut ini:
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.
sumber
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.
sumber
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).
sumber
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
sumber
Perkiraan yang akurat namun cepat dari fungsi sin dan cos secara bersamaan, dalam javascript, dapat ditemukan di sini: http://danisraelmalta.github.io/Fmath/ (diimpor dengan mudah ke c / c ++)
sumber
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.
sumber
Kompilator MSVC dapat menggunakan fungsi SSE2 (internal)
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:
Majelis (untuk x86) dengan / fp: fast:
Majelis (untuk x86) tanpa / fp: cepat tetapi dengan / fp: tepat sebagai gantinya (yang merupakan default) memanggil sin dan cos terpisah:
So / fp: fast adalah wajib untuk optimasi sincos.
Tapi harap dicatat itu
mungkin tidak seakurat
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.
sumber