{"action":"created","issue":{"url":"https://api.github.com/repos/dart-lang/sdk/issues/38648","repository_url":"https://api.github.com/repos/dart-lang/sdk","labels_url":"https://api.github.com/repos/dart-lang/sdk/issues/38648/labels{/name}","comments_url":"https://api.github.com/repos/dart-lang/sdk/issues/38648/comments","events_url":"https://api.github.com/repos/dart-lang/sdk/issues/38648/events","html_url":"https://github.com/dart-lang/sdk/issues/38648","id":500171197,"node_id":"MDU6SXNzdWU1MDAxNzExOTc=","number":38648,"title":"[vm/ffi] Optimize Pointer<Struct> operations","user":{"login":"dcharkes","id":3601775,"node_id":"MDQ6VXNlcjM2MDE3NzU=","avatar_url":"https://avatars1.githubusercontent.com/u/3601775?v=4","gravatar_id":"","url":"https://api.github.com/users/dcharkes","html_url":"https://github.com/dcharkes","followers_url":"https://api.github.com/users/dcharkes/followers","following_url":"https://api.github.com/users/dcharkes/following{/other_user}","gists_url":"https://api.github.com/users/dcharkes/gists{/gist_id}","starred_url":"https://api.github.com/users/dcharkes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcharkes/subscriptions","organizations_url":"https://api.github.com/users/dcharkes/orgs","repos_url":"https://api.github.com/users/dcharkes/repos","events_url":"https://api.github.com/users/dcharkes/events{/privacy}","received_events_url":"https://api.github.com/users/dcharkes/received_events","type":"User","site_admin":false},"labels":[{"id":219928165,"node_id":"MDU6TGFiZWwyMTk5MjgxNjU=","url":"https://api.github.com/repos/dart-lang/sdk/labels/area-vm","name":"area-vm","color":"c7def8","default":false},{"id":1251731808,"node_id":"MDU6TGFiZWwxMjUxNzMxODA4","url":"https://api.github.com/repos/dart-lang/sdk/labels/library-ffi","name":"library-ffi","color":"d4c5f9","default":false}],"state":"open","locked":false,"assignee":{"login":"dcharkes","id":3601775,"node_id":"MDQ6VXNlcjM2MDE3NzU=","avatar_url":"https://avatars1.githubusercontent.com/u/3601775?v=4","gravatar_id":"","url":"https://api.github.com/users/dcharkes","html_url":"https://github.com/dcharkes","followers_url":"https://api.github.com/users/dcharkes/followers","following_url":"https://api.github.com/users/dcharkes/following{/other_user}","gists_url":"https://api.github.com/users/dcharkes/gists{/gist_id}","starred_url":"https://api.github.com/users/dcharkes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcharkes/subscriptions","organizations_url":"https://api.github.com/users/dcharkes/orgs","repos_url":"https://api.github.com/users/dcharkes/repos","events_url":"https://api.github.com/users/dcharkes/events{/privacy}","received_events_url":"https://api.github.com/users/dcharkes/received_events","type":"User","site_admin":false},"assignees":[{"login":"dcharkes","id":3601775,"node_id":"MDQ6VXNlcjM2MDE3NzU=","avatar_url":"https://avatars1.githubusercontent.com/u/3601775?v=4","gravatar_id":"","url":"https://api.github.com/users/dcharkes","html_url":"https://github.com/dcharkes","followers_url":"https://api.github.com/users/dcharkes/followers","following_url":"https://api.github.com/users/dcharkes/following{/other_user}","gists_url":"https://api.github.com/users/dcharkes/gists{/gist_id}","starred_url":"https://api.github.com/users/dcharkes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcharkes/subscriptions","organizations_url":"https://api.github.com/users/dcharkes/orgs","repos_url":"https://api.github.com/users/dcharkes/repos","events_url":"https://api.github.com/users/dcharkes/events{/privacy}","received_events_url":"https://api.github.com/users/dcharkes/received_events","type":"User","site_admin":false}],"milestone":null,"comments":1,"created_at":"2019-09-30T09:53:55Z","updated_at":"2019-10-02T15:58:54Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"Hot loops with structs usually contain loading a struct in the hot loop. For example\r\n\r\n```dart\r\nvoid doStoreInt32(Pointer<VeryLargeStruct> pointer, int length) {\r\n  for (int i = 0; i < length; i++) {\r\n    pointer.elementAt(i).load<VeryLargeStruct>().c = 1;\r\n    // pointer[i].c = 1; // With extension method syntax.\r\n  }\r\n}\r\n```\r\n\r\nThis is currently slow because (1) loading a struct from a pointer is a runtime entry (2) looking up the size of a struct is a runtime entry.\r\n\r\n### 1. Loading a struct from a Pointer\r\n\r\nThe way that we implement this runtime entry is:\r\n\r\n```c++\r\n// Find <class name>.#fromPointer constructor and call it.\r\n```\r\n\r\nhttps://github.com/dart-lang/sdk/tree/master/runtime/lib/ffi.cc\r\n\r\nThe constructor is generated in kernel\r\n\r\n```dart\r\n// Add a constructor which 'load' can use.\r\n// C.#fromPointer(Pointer<Void> address) : super.fromPointer(address);\r\n```\r\n\r\nhttps://github.com/dart-lang/sdk/tree/master/pkg/vm/lib/transformations/ffi_definitions.dart\r\n\r\nIt would be simple to generate IL per call site to call the right constructor, but our recognized methods generate method bodies, not specialized per call site. We could use a hashtable from class to method to make a generic method body, but we would need to populate that hashtable at some point.\r\n\r\n### 2. Looking up the size of a struct\r\n\r\nSame kind of problem: we find the right `#sizeOf` field, and read its value in the runtime entry.\r\n\r\nThis would also be trivial per (non-generic) call site.\r\n\r\n@sjindel-google @mkustermann Any suggestion on how to implement this optimization?"},"comment":{"url":"https://api.github.com/repos/dart-lang/sdk/issues/comments/537561143","html_url":"https://github.com/dart-lang/sdk/issues/38648#issuecomment-537561143","issue_url":"https://api.github.com/repos/dart-lang/sdk/issues/38648","id":537561143,"node_id":"MDEyOklzc3VlQ29tbWVudDUzNzU2MTE0Mw==","user":{"login":"dcharkes","id":3601775,"node_id":"MDQ6VXNlcjM2MDE3NzU=","avatar_url":"https://avatars1.githubusercontent.com/u/3601775?v=4","gravatar_id":"","url":"https://api.github.com/users/dcharkes","html_url":"https://github.com/dcharkes","followers_url":"https://api.github.com/users/dcharkes/followers","following_url":"https://api.github.com/users/dcharkes/following{/other_user}","gists_url":"https://api.github.com/users/dcharkes/gists{/gist_id}","starred_url":"https://api.github.com/users/dcharkes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcharkes/subscriptions","organizations_url":"https://api.github.com/users/dcharkes/orgs","repos_url":"https://api.github.com/users/dcharkes/repos","events_url":"https://api.github.com/users/dcharkes/events{/privacy}","received_events_url":"https://api.github.com/users/dcharkes/received_events","type":"User","site_admin":false},"created_at":"2019-10-02T15:58:54Z","updated_at":"2019-10-02T15:58:54Z","author_association":"CONTRIBUTOR","body":"As discussed with @mkustermann offline, we can optimize the `load` and `sizeOf` of statically known type arguments by rewiring in the frontend.\r\n\r\nWe need to keep supporting generic invocations, at least for `sizeOf`, if we want support `package:ffi` exposing `allocate` with the current signature (https://github.com/dart-lang/sdk/issues/38520):\r\n\r\n```dart\r\nmalloc = // lookedUpSymbol\r\n\r\nPointer<T> allocate<T>(int count){\r\n  final int size = sizeOf<T>() * count;\r\n  final int addr = malloc(size);\r\n  return Pointer.fromAddress(addr);\r\n}\r\n```"}}
{"actor":{"display_login":"dcharkes"}}