{"action":"closed","issue":{"url":"https://api.github.com/repos/opencv/opencv/issues/7523","repository_url":"https://api.github.com/repos/opencv/opencv","labels_url":"https://api.github.com/repos/opencv/opencv/issues/7523/labels{/name}","comments_url":"https://api.github.com/repos/opencv/opencv/issues/7523/comments","events_url":"https://api.github.com/repos/opencv/opencv/issues/7523/events","html_url":"https://github.com/opencv/opencv/issues/7523","id":183906038,"number":7523,"title":"core: performance test OCL_DivFixture_Divide.Divide fails on ARM","user":{"login":"tomoaki0705","id":3737315,"avatar_url":"https://avatars.githubusercontent.com/u/3737315?v=3","gravatar_id":"","url":"https://api.github.com/users/tomoaki0705","html_url":"https://github.com/tomoaki0705","followers_url":"https://api.github.com/users/tomoaki0705/followers","following_url":"https://api.github.com/users/tomoaki0705/following{/other_user}","gists_url":"https://api.github.com/users/tomoaki0705/gists{/gist_id}","starred_url":"https://api.github.com/users/tomoaki0705/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomoaki0705/subscriptions","organizations_url":"https://api.github.com/users/tomoaki0705/orgs","repos_url":"https://api.github.com/users/tomoaki0705/repos","events_url":"https://api.github.com/users/tomoaki0705/events{/privacy}","received_events_url":"https://api.github.com/users/tomoaki0705/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/opencv/opencv/labels/bug","name":"bug","color":"fc2929"},{"url":"https://api.github.com/repos/opencv/opencv/labels/test","name":"test","color":"fbca04"}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2016-10-19T09:08:53Z","updated_at":"2016-10-20T09:37:27Z","closed_at":"2016-10-20T09:37:27Z","body":"##### System information (version)\r\n- OpenCV => master (8213e57f3b032033b6c4c1e3eb3bdc84c12c0e1a)\r\n- Operating System / Platform => ARM 32bit (Jetson TK1)\r\n- Compiler => gcc 4.8.4\r\n\r\n##### Summary of the issue\r\n- Divide using NEON is too aggressive of losing precision\r\n- I'd like to ask a suggestion of way to fix it\r\n\r\n##### Detailed description\r\n- opencv_perf_core fails on OCL_DivFixture_Divide.Divide\r\n```\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/0, where GetParam() = (640x480, 8UC1)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/2, where GetParam() = (640x480, 8UC3)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/4, where GetParam() = (640x480, 8UC4)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/6, where GetParam() = (1280x720, 8UC1)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/8, where GetParam() = (1280x720, 8UC3)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/10, where GetParam() = (1280x720, 8UC4)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/14, where GetParam() = (1920x1080, 8UC3)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/16, where GetParam() = (1920x1080, 8UC4)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/18, where GetParam() = (3840x2160, 8UC1)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/20, where GetParam() = (3840x2160, 8UC3)\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/22, where GetParam() = (3840x2160, 8UC4)\r\n```\r\n- The failure is because the values are slightly different (expected value was **2** when the actual value was **1**)\r\n```\r\n[ RUN      ] OCL_DivFixture_Divide.Divide/0\r\n/home/ubuntu/opencv-fork/modules/ts/src/ts_perf.cpp:377: Failure\r\nThe difference between expect_last and actual_last is 1, which exceeds eps, where\r\nexpect_last evaluates to 2,\r\nactual_last evaluates to 1, and\r\neps evaluates to 0.00025499999999999996.\r\nArgument \"dst\" has unexpected value of the last element\r\n\r\nparams    = (640x480, 8UC1)\r\ntermination reason:  unknown\r\nbytesIn   =     614400\r\nbytesOut  =     307200\r\nsamples   =         13 of 100\r\noutliers  =          1\r\nfrequency = 1000000000\r\nmin       =     640664 = 0.64ms\r\nmedian    =     644664 = 0.64ms\r\ngmean     =     644412 = 0.64ms\r\ngstddev   = 0.00548082 = 0.02ms for 97% dispersion interval\r\nmean      =     644421 = 0.64ms\r\nstddev    =       3539 = 0.00ms\r\n[  FAILED  ] OCL_DivFixture_Divide.Divide/0, where GetParam() = (640x480, 8UC1) (27 ms)\r\n```\r\n- I digged in a bit, and OCL_DivFixture goes to [here ](https://github.com/opencv/opencv/blob/8213e57f3b032033b6c4c1e3eb3bdc84c12c0e1a/modules/core/src/arithm.cpp#L3157-L3165)\r\n```\r\nvoid div8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2,\r\n                   uchar* dst, size_t step, int width, int height, void* scale)\r\n{\r\nCALL_HAL(div8u, cv_hal_div8u, src1, step1, src2, step2, dst, step, width, height, *(const double*)scale\r\n```\r\n- and this function will call ```div``` in ```carotene``` which is [here](https://github.com/opencv/opencv/blob/8213e57f3b032033b6c4c1e3eb3bdc84c12c0e1a/3rdparty/carotene/src/div.cpp#L127) \r\n```\r\ntemplate <typename T>\r\nvoid div(const Size2D &size,\r\n         const T * src0Base, ptrdiff_t src0Stride,\r\n         const T * src1Base, ptrdiff_t src1Stride,\r\n         T * dstBase, ptrdiff_t dstStride,\r\n         f32 scale,\r\n         CONVERT_POLICY cpolicy)\r\n{\r\ninternal::assertSupportedConfiguration();\r\n```\r\n- and calls the function ```divSaturateQ``` which is [here](https://github.com/opencv/opencv/blob/8213e57f3b032033b6c4c1e3eb3bdc84c12c0e1a/3rdparty/carotene/src/div.cpp#L65)\r\n```\r\n{ return vcvtq_s32_f32(vmulq_f32(vmulq_n_f32(vcvtq_f32_s32(v1), scale), internal::vrecpq_f32(vcvtq_f32_s32(v2)))); }\r\n```\r\n- Now the real cause is that the divide is computed by [reciprocal value ](https://github.com/opencv/opencv/blob/8213e57f3b032033b6c4c1e3eb3bdc84c12c0e1a/3rdparty/carotene/src/intrinsics.hpp#L53)\r\n```\r\ninline float32x4_t vrecpq_f32(float32x4_t val)\r\n{\r\n    float32x4_t reciprocal = vrecpeq_f32(val);\r\n    reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal);\r\n    reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal);\r\n    return reciprocal;\r\n}\r\n```\r\n- Approach of using Newton-Raphson method with reciprocal is quite useful to accelerate, but it causes difference in some cases.\r\n- I wrote a [small program](https://github.com/tomoaki0705/sampleReciprocalNeon) to verify that, and based on this program, more than 200 cases causes difference in range of ```char``` (i.e. -128---128)\r\n- The ```actual``` value below shows the result using ```vrecpeq``` and ```expected``` show the result from normal float operation and then rounded to int.\r\n```\r\niteration:2 expected:1 actual:0 -126/-126\r\niteration:2 expected:1 actual:0 -125/-125\r\niteration:2 expected:2 actual:1 -126/-63\r\niteration:2 expected:3 actual:2 -126/-42\r\niteration:2 expected:6 actual:5 -126/-21\r\niteration:2 expected:-6 actual:-5 -126/21\r\niteration:2 expected:-3 actual:-2 -126/42\r\niteration:2 expected:-2 actual:-1 -126/63\r\niteration:2 expected:-1 actual:0 -125/125\r\niteration:2 expected:-1 actual:0 -126/126\r\niteration:2 expected:1 actual:0 -122/-122\r\niteration:2 expected:1 actual:0 -121/-121\r\niteration:2 expected:2 actual:1 -122/-61\r\n```\r\n- Now, I think there are three ways to fix this issue\r\n 1. loosen the threshold on performance test of division\r\n 2. deactivate the NEON optimization just for division\r\n 3. avoid including this test case on ARM\r\n- In the past, I was suggested to loosen the threshold only for ARM platform, but in this case, I don't agree about loosing the threshold.  The result differs in int, not float.\r\n- At least for division on integer types, I propose to deactivate any NEON optimization on this.\r\n\r\n##### Steps to reproduce\r\n- Run ```opencv_perf_core --gtest_filter=OCL_DivFixture_Divide.Divide*```\r\n\r\n"}}
{"actor":{"display_login":"opencv-pushbot"}}